How to use swift's firstIndex to generate a new subarray? - ios

For example, I have array let candidates=["1","0","a","b","c"] , and I want to return ["a","b","c"]
Here's the code:
if let head = candidates.firstIndex(of: "0") {
return candidates[head..<candidates.count]
}
But got error: No 'subscript' candidates produce the expected contextual result type '[String]'

Does your function expect to return type [String]?
candidates[head..<candidates.count] will return type ArraySlice so if you want to convert that to an array, you might need to do
return Array(candidates[head..<candidates.count])
One more small addition for completeness, since you want to return ["a","b","c"], you will need to start from the index after "0" so I would do:
return Array(candidates[head+1..<candidates.count])

Related

iOS Swift 3 - Argument labels '(of:)' do not match any available overloads Error

I'm getting the error message Argument labels '(of:)' do not match any available overloads. Below is the code I'm using.
let prefs = UserDefaults.standard
var id: String!
if var array = prefs.string(forKey: "myArray"){
if let index = array.index(of: id) {
array.remove(at: index)
prefs.setValue(array, forKey: "myArray")
}
}
I've seen a lot of answers on Stack Overflow with very similar code to that. So I'm not quite sure why this wouldn't be working.
Basically I'm just trying to remove the element in the array that = id then set that new array to the user defaults.
Update
Just updated the code above to show how array is getting defined. id is a string that is defined in a separate section.
By accessing prefs.string(forKey: "myArray"), you are getting a String, not an array of strings. You should use this:
if var prefs.array(forKey: "myArray") as? [String] { }
or
if var prefs.value(forKey: "myArray") as? [String] { }
Make sure to not forget putting as! [String], because the first method returns [Any], an which can contain objects of any type, not specifically String. Then your error should be solved, because index(of: ) can only be used on Arrays of specified types.
Hope it helps!
Just make an alt + Click on an "array" variable to make sure it is of type Array ([String]), not a String. To apply .index(of:) method it must be an array.
Like this:
String does not have a method .index(of:). That's what the error is pointing at. And sure make a cast to [String]? if it fits.

Array.map() produces '[T]', not the expected contextual result type '[String: Any?]'

I'm writing an extension to bridge the dictionary values between FirebaseDatabase and Eureka.
private extension Dictionary {
func firebaseFriendlyDictionary() -> [String: Any?] {
return self.map({ (key: String, value: Any?) -> (String, Any?) in
if value is NSDate {
return (key, (value as! NSDate).timeIntervalSince1970)
}
return (key, value)
})
}
}
But I get thrown this error when I try to build:
map produces '[T]', not the expected contextual result type '[String: Any?]'
Your problem lies with the fact, that map always returns an Array, even when applied on a Dictionary. Your error message basically means, that you declared your method as returning a Dicitonary, but the statement inside returns an Array ([T] - means an Array with objects of some type T). In your case, the array returned by map will contain tuples (more about them here). In this case it looks like a key value pair, but its not equivalent to a key-value pair inside a Dictionary. Basically, tuples enable you to return more than one value/object from method. You can think of them as of anonymous structures.
In my opinion, there is no need to use a map to accomplish what you need - the solution provided by Mr. Xcoder is the way to go.
If you really want to use something functional like map, the method you actually want is reduce.
I'll demonstrate. To make things as clear as possible, I think it will help if we separate out the transformation to which your values are being subjected into a function of its own:
func dateToSeconds(_ thing:Any?) -> Any? {
guard let date = thing as? Date else {return thing}
return date.timeIntervalSince1970
}
Okay, so here's our test dictionary:
let d1 : [String:Any?] = ["1":Date(), "2":"two", "3":15, "4":true]
Now we're ready to apply reduce. It passes two parameters into its function. The first is the "accumulator" where we keep building up the ultimate result, which in this case is another dictionary. The second is an element of the original dictionary, represented as a tuple of key-value pairs called key and value:
let d2 = d1.reduce([String:Any?]()) { (dict, tuple) in
var dict = dict
dict[tuple.key] = dateToSeconds(tuple.value)
return dict
}
And when we examine d2, we see that we have got the right answer:
d2 // ["3": {some 15}, "2": {some "two"}, "1": {some 1486228695.557882}, "4": {some true}]
I couldn't figure out how to fix that error, nor have I succeeded achieving the desired result with an extension or a map(), but I have an alternative solution to the problem, using a function:
Declaring Dictionary:
var date = NSDate()
var swiftDict:[String : Any?] = ["1": date, "2": "two", "3": 15, "4": true]
Function:
func firebaseFriendlyDictionary(_ dict: [String : Any?]) -> [String : Any?]{
var Dict = dict
for (key, value) in dict
{
if (value is NSDate){
Dict[key] = (value as! NSDate).timeIntervalSince1970
}
}
return Dict
}
Usage:
swiftDict = firebaseFriendlyDictionary(swiftDict)
Testing:
Assuming that we have the date 2017-02-04 16:42:46 +0000 the output is 1486226566.2349629, which is correct.
Why not mapping the Dictionary? As Losiowaty pointed in his excellent answer, map always returns an Array, in this case an Array of Tuples([T]). In my opinion a map function is not needed in this context, plus it requires more code to accomplish.
Hope this helps!

Swift generic array to specific type

I am writing a function that needs to return an array of items. In the function there will be some logic that will determine the type of the items I want to return from the function. I started something like this:
func testFunc<T>(option:Int) -> [T] {
var result:[T] = []
switch option {
case 1:
let sampleString:String = "hello"
result.append(sampleString)
case 2:
let sampleInt:Int = 23
result.append(sampleInt)
default:
return result
}
return result
}
This gives me the following errors:
"cannot invoke append with an argument list of type '(String)',
and
"cannot invoke append with an argument list of type '(Int)'
It makes sense, but, I am trying to figure out how to solve my problem. When I call the function I don't know what type will be in the array returned, but the function will know how to determine the type before it start appending items to the array.
How can I accomplish this in Swift?
Swift does not support variables with multiple types. Using a generic T makes the function generic so the return type can be chosen by the caller.
You should rethink your design.
If you really want to return multiple result types based on a parameter, there are a few workarounds:
1: Use an Any array.
var result:[Any] = []
2: Use an enum with associated values
enum TestFuncResult {
case string(String)
case int(Int)
}
func testFunc(option: Int) -> [TestFuncResult] {
var result:[TestFuncResult] = []
switch option {
case 1:
let sampleString:String = "hello"
result.append(.string(sampleString))
case 2:
let sampleInt:Int = 23
result.append(.int(sampleInt))
default:
return result
}
return result
}

Ambiguous error when attempting to filter values using enum

I have a filter that I am trying to use to compare one value to another. Here is the enum that I am using:
enum SomeEnum: String {
case first = "Hey"
case second = "There"
case third = "Peace"
static let values = [first, second, third]
func pickOne() -> String {
switch self {
case .first:
return "value 1"
case .second:
return "value 2"
case .third:
return "value 3"
}
}
Here is where I am attempting to filter and find matching values:
array.append(SomeEnum.values.filter({$0.rawValue == anotherArray["id"] as! String}))
I end up getting an ambiguous error:
Cannot convert value of type '[SomeEnum]' to expected argument type 'String'
Any ideas?
The problem is, that SomeEnum.values return type is [SomeEnum] and not String.
And the append function expects the parameter to be String, instead it is [SomeEnum].
This is, what you need to change:
Change append to appendContentsOf, since filter function returns an array, and not a single value
Change the [SomeEnum] to [String] since you are adding it to a [String] array, like this.
This is the fix:
array.appendContentsOf(SomeEnum.values.filter({ $0.rawValue == "SomeString" }).map({ $0.PickOne() }))

"Cannot subscript a value of type '[String]' with an index of type 'String'

I'm trying to display a dynamically updated array in a label using:
for i in result {
outputLbl.text = result[i].joinWithSeparator("\n")
}
However I get the error
Cannot subscript a value of type '[String]' with an index of type 'String'.
Any idea how I can fix this?
Note that when using the loop "header" for X in Y, you don't get the indices of Y, but the actual elements of Y. Judging from your error, results is an array of strings ([String]). Hence, i in you for loop represents---one by one---the elements in the String array results.
So, if you wanted to access the string elements one by one in the for loop above, you could use the approach in your example:
let result = ["Hello", "World"]
for myString in result {
// use each string element in some manner...
}
However, as you are using the array method joinWithSeparator(..), you should use, just as Leo writes in his comment above, this method directly on your array (and not their elements!)
let result = ["Hello", "World"]
outputLbl.text = result.joinWithSeparator("\n")
/* Hello\nWorld */
I think what you are doing here is trying to iterate through an array of string and then update a label that is in this case "outputLbl".Here you can do something like this
for i in result {
//result is array of strings.
// here i is individual element of result array
/* outputLbl.text = result[i].joinWithSeparator(ā€œ\nā€)*/
//instead you can write
outputLbl.text = i.joinWithSeparator(ā€œ\nā€)
}
The reason you are getting this error is as follows:
It seems you are confusing the type of i. The variable result is of type [String] which means it is an array of String types. By virtue of being an array, it must be subscripted with an Int, not a String. So something like result[0] or result[12] is valid, but something like result["hello"] is not valid. The variable i here is a String because it is a single element in an array of String types, which means that effectively, what you're trying to do by saying result[i] is something along the lines of result["hello"].
That having been said, the true solution to your problem is that the method joinWithSeparator(_:String) is not a String method but rather a Sequence type method. Which means it should be called on a Sequence like an object of type [String]. So what you should use is:
outputLbl.text = result.joinWithSeparator("\n")
What's going on here is the compiler is inferring i to be of type String since that's what result is. You should be more verbose in your naming conventions.
for specificString in result {
outputLbl1.text += "\n\(specifcString)"
}
EDITED for correctness.

Resources