Filter Array if Current Time is within TimeRange - swift

My current function filters the array and returns an array of PFObjects with only "Type" = "Sushi". Now, I am trying to filter the array if current time is within a time range ("OpenHours" and "CloseHours")
The new Function passes dayOfWeek: Int, timeNow: String
"OpenHours" Example: [0, 6] = [sunday, monday]
["0011","0011","0011","0011","0011","0011","0011"]
"CloseHours" Example:
["2350","2350","2350","2350","2350","2350","2350"]
Current Function that filters "Type":
func filterRestaurants(filteredObject: String) {
//func filterOpenNow(dayOfWeek: Int, timeNow: String){
filteredRestaurantArray = unfilteredRestaurantArray.filter() {
if let type = ($0 as PFObject)["Type"] as? String { // Get value of PFObject
return type.rangeOfString("Sushi") != nil
} else {
println("nope")
return false
}
}
Basically I need to filter for objects where current time timeNow is between OpenHours and CloseHours for a given dayOfWeek
Edit:
What I've Tried so far:
I'm unsure how to get the position of a value in the PFObject array. Normal I would check if the timeNow is between OpenNow[dayOfWeek] and CloseNow[dayOfWeek]
Something like this (except with filter):
if OpenNow[dayOfWeek] ... CloseNow[dayOfWeek] ~= timeNow {
println("success")
}

I am not entirely sure what the properties of your PFObject instance are, so I'm going to make some assumptions and you will have to correct the keys to meet your needs.
Working with the function signature you provided is not entirely possible. This is because in order for it to work you would be accessing a list of PFObjects that were not provided to the function. While Swift does not keep you from doing that, it is generally not a wise design choice as you cannot be confident of the results of the function at any given point in time. So, to provide what is called referential transparency we will modify your function to also take in the collection of PFObjects.
With that collection we will then use the native filter function to determine which ones to return from the function. The filter function takes a collection and a closure. The closure takes one parameter, an element of the collection, and return a Bool. The closure is what determines if an element is kept or discarded.
Knowing that, we can build up the functionality you requested. I cannot guarantee there isn't a better way to do the first if let dance at the beginning, but it should get the job done.
func filterAreOpen(restaurants: [PFObject], forTime time: String, onDay day: Int) -> [PFObject] {
let openRests = filter(restaurants) { r in
if let openHours = r["OpenHours"] as AnyObject? as? [String] {
if let closeHours = r["CloseHours"] as AnyObject? as? [String] {
switch (openHours[day].toInt(), closeHours[day].toInt(), time.toInt()) {
case let (.Some(oh), .Some(ch), .Some(t)):
return oh...ch ~= t
default:
return false
}
}
}
return false
}
return openRests
}
And you would use it like this
let rests = [ // example objects that are replaced by your PFObject instances
[
"OpenHours":["0011","0012","0013"],
"CloseHours":["0023","0023","0023"],
"Name":"Restaurant1"
],
[
"OpenHours":["0014","0015","0016"],
"CloseHours":["0020","0020","0020"],
"Name":"Restaurant2"
]
]
let openRestaurants = filterAreOpen(rests, forTime: "0012", onDay: 1)
/* Results
[{
CloseHours = (
0023,
0023,
0023
);
Name = Restaurant1;
OpenHours = (
0011,
0012,
0013
);
}]
*/
Edit:
A quick explanation about the switch inside the closure. In Swift the switch statement is much more powerful than it was in the Objective-C days. It is capable of matching a value against a pattern, not just numbers.
So, in this case, the switch is matching a 3-tuple of Int?, (Int?, Int?, Int?). This is because String's toInt() method returns an Int?. switch is also able to bind matched patterns to local scoped constants. To do that we use the let keyword. To pattern match on an Optional value, you use the .Some(x) pattern. Since we have a 3-tuple we use .Some(x) three time, with the x replaced by some meaningful name. This way we have access to the three values we are interested in if the three toInt() calls evaluated to non-nil values.
If any of the String values could not evaluate to an Int, and so was nil, the default case is used, and returns false.
You can view the language book on conditional statements here.

Related

Append Values in NSSet To An Array of Type [String]

I am attempting to get the values from an NSSet in core data and append those values to an array of type String.
func addStepsToArray() {
if let steps = entity.steps {
for i in steps {
recipeStep.append(String(i))
}
}
}
entity.steps is the list of steps tied to a core data entity. This is an NSSet. I am trying to copy those values to an array of type String.
#State var recipeStep: [String]
When trying to do this in my for in loop, I receive the following error: No exact matches in call to initializer
If I remove the conversion of "I" to String, I receive the following error:
Cannot convert value of type NSSet.Element (aka Any) to expected argument type String
Any idea on how to get this to work?
NSSet is defined in Objective C, which didn't have generics. It's an untyped collection, so you don't statically know anything about its elements.
As you've noticed, your i variable isn't a String, it's an Any.
You're confusing type coercion ("casting") with type conversion. If i were a Double, you could call String(i) to invoke an initializer which takes a double, and processes into a String.
You tried something similar by calling String(i), where you're making the Swift compiler find an initializer on String with the signitiure init(_: Any).
There is no such initializer. And besides, that's not what you want. You don't want to create a new String from a different kind of value. You already have a string, it's just "hidden" behind an Any reference.
What you're looking for is to do a down-cast, from Any to String:
func addStepsToArray() {
if let steps = entity.steps {
for i in steps {
guard let step = i as? String else {
fatalError("Decide what to do if the value isn't a String.")
}
recipeStep.append(i as String)
}
}
}
I'll warn you though, there are several issues/blemishes with this code:
You're using a for loop to do what is ultimately just a mapping operation
Your computation doesn't return its ouput, and instead indirectly achieves its goal through a side-effect on recipeStep
Your computation doesn't take a its input as a parameter, and instead indirectly achieves its goal through a side-effect on entity
i is conventionally expected to be an integer index of a for loop iterating over a sequence of numbers. Here it's an Any (a String at runtime)
Here's what I would suggest instead:
func getRecipeSteps(from entity: MyEntityType) -> [String] {
guard let steps = entity.steps else { return [] }
return steps.map { step in
guard let stringStep = step as? String else {
fatalError("Decide what to do if the value isn't a String.")
}
return step
}
}
Then in the rest of your code (and your tests), you can write self.recipeSteps = getRecipeSteps(from: myEntity). Elegant!
If you're certain that these entity.steps values can only ever be strings, then you can boil this down to a single map with a force-cast:
func getRecipeSteps(from entity: MyEntityType) -> [String] {
entity.steps?.map { $0 as! String } ?? []
}
Just convert directly:
let set = Set(["1", "2", "3"])
let array = Array(set)
DDLog(set)//Set<String>)
DDLog(array)//[String]

Check if string contains optional string in Swift, but only if it's not nil

I'd like to add an optional "filter" parameter to a function that processes a list of strings. If the filter is nil, I'd like to process all the strings, otherwise I'd only like to process the ones that contain the filter. Roughly:
func process(items: [String], filter: String?) {
for item in items {
if filter == nil || item.contains(filter) {
// Do something with item
}
}
}
The typechecker complains about passing filter into contains since it's optional, but contains takes a String. I could of course force-unwrap, but that seems ugly. The above would compile in Kotlin because it would smart-cast away the optional, but what's the idiomatic way to express this in Swift?
Thanks!
There is a map on Optional that executes the provided closure only if the optional is not nil. You can use map along with the nil coalescing operator ?? to provide the default value of true if the map returns nil because the filter is nil:
func process(items: [String], filter: String?) {
for item in items {
if filter.map(item.contains) ?? true {
// process item
}
}
}
The key here is (and always was going to be) the nil-coalescing operator. This lets you unwrap an Optional safely if it is not nil, but if it is nil, you substitute another value.
The issue with the question, however, is that it poses itself in an unSwifty way. You would never write a filtering function that takes an Optional String. You would write a filtering function that takes an Optional filtering function! Instead of limiting the caller to contains and a String, you want to allow any type of array and any filtering function. That is the Swifty way to write this function.
And while we are at it, the function should not predetermine what is done with the members of the array either. That should be another function that the caller passes in!
Thus we end up with this far more general and much Swiftier statement of the goal:
func process<T>(_ arr: [T],
filtering filterPred: ((T)->Bool)? = nil,
processing processPred: ((T)->Void)) {
// ...
}
Okay! So let's write this function. The processing part is easy; this is the predicate to a forEach call. Like this:
func process<T>(_ arr: [T],
filtering filterPred: ((T)->Bool)? = nil,
processing processPred: ((T)->Void)) {
arr.forEach(processPred)
}
Very good! But we have neglected to deal with the filtering function. Let's take care of that. Clearly, this is where the rubber meets the road. And just as clearly, it is evident that this is a function to be passed into filter. But we can only do that by unwrapping if it is not nil. What if it is nil? The OP has specified that in that case we should let all element through the filter.
So consider a predicate function that is to be passed into filter. What is the function that lets everything through? It is:
{ _ in true }
So that is what to put after the nil-coalescing operator! Therefore, this is the full answer:
func process<T>(_ arr: [T],
filtering filterPred: ((T)->Bool)? = nil,
processing processPred: ((T)->Void)) {
arr.filter(filterPred ?? { _ in true }).forEach(processPred)
}
And here are two tests:
let arr = [1,2,3,4]
self.process(arr) { print($0) }
self.process(arr) { $0 % 2 == 0 } processing: { print($0) }

Binary operator '==' cannot be applied to operands of type '[String]' and 'String

I am new to coding and I needed some help with answering this challenge.
Instructions:
// Given the two arrays below, write a function that takes a String as an input parameter and returns a Boolean value. The function should return true if the String input is in either array and it should return false if the String input is in neither array.
//
// Examples:
// Call your function and pass in the String "cat" as the input. Your function should return true
// Call your function and pass in the String "cow" as the input. Your function should return false
let array1 = ["dog", "cat", "bird", "pig"]
let array2 = ["turtle", "snake", "lizard", "shark"]
// Write your function below:
Here is where I have written my function:
func test(Animal:String){
let list = array1 + array2
for animal in list {
if list == animal {
print("true")
} else {
print("false")
}
}
}
test(Animal: "dog")
The error I am getting is:
Binary operator '==' cannot be applied to operands of type '[String]' and 'String under the if statement.
Help if you can and I apologize in advance if this is not formatted correctly.
Thanks!
Just a tip, in the future you should try adding a more descriptive question title, that is super vague and could mean anything.
Anyways your issue is with the line:
if list == animal {
The error you got is quite specific and tells you exactly what's going wrong. [String] is an array of strings while String is just a single item. So when you write for animal in list your taking one animal in that list at a time.
Comparing an animal to a list is not allowed. They are not the same type ([String] and String are different types, as the compiler error told you). You can only compare variables of the same type. So in your case a list (String array aka [String]) to a another list, or an animal (String) to an animal.
Now what you want to do is see if a string you got is in either array. There's actually a built in method in arrays that lets you do exactly this: .contains
func test(animal:String){
let list = array1 + array2
if list.contains(animal) {
print("true")
} else {
print("false")
}
}
or if you want to be extra concise with some good code you can try
func test(animal:String){
let list = array1 + array2
print(list.contains(animal)) // will be false if it doesn't so prints false, true if it does so prints true.
}
And another side note.. You want to be very careful doing an if else inside of a for loop where both the if and the else return. You shouldn't be making decisions after looking at only ONE element.
Because typically you want to check EVERY value before printing false, but when you have an if else, you'll return/print after checking ONLY the first value.
if you wanted to do it your way (no built in methods, iterating through the array) you would want to do something like this:
func test(animal:String){
let list = array1 + array2
for candidate in list {
if animal == candidate { // there's a match! we can return true
print("true ")
return // return says to exit the function since we no longer need to keep going this is appropriate
}
} // for loop ends
// we only return false once the ENTIRE for loop has run, because we know if it did exist, we would never get to this point in the code
// this part only runs once EVERY item has been checked and none were a match
print("false")
}
And finally as bilal says, never start variable names with capital letters. Typically capital letters mean its a type (like String). You'll notice I renamed Animal -> animal for you in my examples

How to handle initial nil value for reduce functions

I would like to learn and use more functional programming in Swift. So, I've been trying various things in playground. I don't understand Reduce, though. The basic textbook examples work, but I can't get my head around this problem.
I have an array of strings called "toDoItems". I would like to get the longest string in this array. What is the best practice for handling the initial nil value in such cases? I think this probably happens often. I thought of writing a custom function and use it.
func optionalMax(maxSofar: Int?, newElement: Int) -> Int {
if let definiteMaxSofar = maxSofar {
return max(definiteMaxSofar, newElement)
}
return newElement
}
// Just testing - nums is an array of Ints. Works.
var maxValueOfInts = nums.reduce(0) { optionalMax($0, $1) }
// ERROR: cannot invoke 'reduce' with an argument list of type ‘(nil, (_,_)->_)'
var longestOfStrings = toDoItems.reduce(nil) { optionalMax(count($0), count($1)) }
It might just be that Swift does not automatically infer the type of your initial value. Try making it clear by explicitly declaring it:
var longestOfStrings = toDoItems.reduce(nil as Int?) { optionalMax($0, count($1)) }
By the way notice that I do not count on $0 (your accumulator) since it is not a String but an optional Int Int?
Generally to avoid confusion reading the code later, I explicitly label the accumulator as a and the element coming in from the serie as x:
var longestOfStrings = toDoItems.reduce(nil as Int?) { a, x in optionalMax(a, count(x)) }
This way should be clearer than $0 and $1 in code when the accumulator or the single element are used.
Hope this helps
Initialise it with an empty string "" rather than nil. Or you could even initialise it with the first element of the array, but an empty string seems better.
Second go at this after writing some wrong code, this will return the longest string if you are happy with an empty string being returned for an empty array:
toDoItems.reduce("") { count($0) > count($1) ? $0 : $1 }
Or if you want nil, use
toDoItems.reduce(nil as String?) { count($0!) > count($1) ? $0 : $1 }
The problem is that the compiler cannot infer the types you are using for your seed and accumulator closure if you seed with nil, and you also need to get the optional type correct when using the optional string as $0.

swift, optional unwrapping, reversing if condition

Let's say I have function which returns optional. nil if error and value if success:
func foo() -> Bar? { ... }
I can use following code to work with this function:
let fooResultOpt = foo()
if let fooResult = fooResultOpt {
// continue correct operations here
} else {
// handle error
}
However there are few problems with this approach for any non-trivial code:
Error handling performed in the end and it's easy to miss something. It's much better, when error handling code follows function call.
Correct operations code is indented by one level. If we have another function to call, we have to indent one more time.
With C one usually could write something like this:
Bar *fooResult = foo();
if (fooResult == null) {
// handle error and return
}
// continue correct operations here
I found two ways to achieve similar code style with Swift, but I don't like either.
let fooResultOpt = foo()
if fooResult == nil {
// handle error and return
}
// use fooResultOpt! from here
let fooResult = fooResultOpt! // or define another variable
If I'll write "!" everywhere, it just looks bad for my taste. I could introduce another variable, but that doesn't look good either. Ideally I would like to see the following:
if !let fooResult = foo() {
// handle error and return
}
// fooResult has Bar type and can be used in the top level
Did I miss something in the specification or is there some another way to write good looking Swift code?
Your assumptions are correct—there isn't a "negated if-let" syntax in Swift.
I suspect one reason for that might be grammar integrity. Throughout Swift (and commonly in other C-inspired languages), if you have a statement that can bind local symbols (i.e. name new variables and give them values) and that can have a block body (e.g. if, while, for), those bindings are scoped to said block. Letting a block statement bind symbols to its enclosing scope instead would be inconsistent.
It's still a reasonable thing to think about, though — I'd recommend filing a bug and seeing what Apple does about it.
This is what pattern matching is all about, and is the tool meant for this job:
let x: String? = "Yes"
switch x {
case .Some(let value):
println("I have a value: \(value)")
case .None:
println("I'm empty")
}
The if-let form is just a convenience for when you don't need both legs.
If what you are writing is a set of functions performing the same sequence of transformation, such as when processing a result returned by a REST call (check for response not nil, check status, check for app/server error, parse response, etc.), what I would do is create a pipeline that at each steps transforms the input data, and at the end returns either nil or a transformed result of a certain type.
I chose the >>> custom operator, that visually indicates the data flow, but of course feel free to choose your own:
infix operator >>> { associativity left }
func >>> <T, V> (params: T?, next: T -> V?) -> V? {
if let params = params {
return next(params)
}
return nil
}
The operator is a function that receives as input a value of a certain type, and a closure that transforms the value into a value of another type. If the value is not nil, the function invokes the closure, passing the value, and returns its return value. If the value is nil, then the operator returns nil.
An example is probably needed, so let's suppose I have an array of integers, and I want to perform the following operations in sequence:
sum all elements of the array
calculate the power of 2
divide by 5 and return the integer part and the remainder
sum the above 2 numbers together
These are the 4 functions:
func sumArray(array: [Int]?) -> Int? {
if let array = array {
return array.reduce(0, combine: +)
}
return nil
}
func powerOf2(num: Int?) -> Int? {
if let num = num {
return num * num
}
return nil
}
func module5(num: Int?) -> (Int, Int)? {
if let num = num {
return (num / 5, num % 5)
}
return nil
}
func sum(params: (num1: Int, num2: Int)?) -> Int? {
if let params = params {
return params.num1 + params.num2
}
return nil
}
and this is how I would use:
let res: Int? = [1, 2, 3] >>> sumArray >>> powerOf2 >>> module5 >>> sum
The result of this expression is either nil or a value of the type as defined in the last function of the pipeline, which in the above example is an Int.
If you need to do better error handling, you can define an enum like this:
enum Result<T> {
case Value(T)
case Error(MyErrorType)
}
and replace all optionals in the above functions with Result<T>, returning Result.Error() instead of nil.
I've found a way that looks better than alternatives, but it uses language features in unrecommended way.
Example using code from the question:
let fooResult: Bar! = foo();
if fooResult == nil {
// handle error and return
}
// continue correct operations here
fooResult might be used as normal variable and it's not needed to use "?" or "!" suffixes.
Apple documentation says:
Implicitly unwrapped optionals are useful when an optional’s value is confirmed to exist immediately after the optional is first defined and can definitely be assumed to exist at every point thereafter. The primary use of implicitly unwrapped optionals in Swift is during class initialization, as described in Unowned References and Implicitly Unwrapped Optional Properties.
How about the following:
func foo(i:Int) ->Int? {
switch i {
case 0: return 0
case 1: return 1
default: return nil
}
}
var error:Int {
println("Error")
return 99
}
for i in 0...2 {
var bob:Int = foo(i) ?? error
println("\(i) produces \(bob)")
}
Results in the following output:
0 produces 0
1 produces 1
Error
2 produces 99