Cast Array<Any> to Array<Int> in Swift - swift

I have array of Any objects called timestamps, in which first object is string, and rest objects are Int. What i want is, to drop first element and treat rest of array as Array of Int objects..
I tried:
let timestamps = xAxisData.dropFirst()
if let timestamps = timestamps as? Array<Int> {
}
It compile, but it says - Cast from 'ArraySlice<Any>' to unrelated type 'Array<Int>' always fails
I could possibly iterate through array and create new temporary array with check every element whether it Int or not, but i think there is a better cleaner way?

You need to create an Array from the ArraySlice created by dropFirst.
let timestamps = Array(xAxisData.dropFirst())
if let timestamps = timestamps as? [Int] {
}

I like to use something like a flatMap so no matter were the non-Int values are in the array they are not consider for the resulting array.
let originalArray:[Any] = ["No int", 2, 3, "Also not int", 666, 423]
let intArray:[Int] = originalArray.flatMap { $0 as? Int }
print(intArray)
Will print this:
[2, 3, 666, 423]
So in your case you could do something like:
let timestamps = xAxisData.flatMap { $0 as? Int }
As stated by Steve Madsen in the comment below, if you are using Swift 4.1 or newer Apple suggest to used compactMap for getting rid of optional nils so that specific use of flatMap is deprecated.
let timestamps = xAxisData.compactMap { $0 as? Int }

Here is another method to convert a subset of an array with mixed types to something more specific:
let intArray = anyArray.compactMap { $0 as? Int }

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]

How to initialize a struct with dictionaries in Swift

I want to initialize every time a struct with dictionaries. Later, I'm going to use its properties instead a dictionary's keys and values - it seems rather easier. However, when I try the code below, it tells me that "Return from initializer without initializing all stored properties" and "1. 'self.one' not initialized" and "2. 'self.two' not initialized". My question is how to initialize a struct from a dictionary, so that I have basically a struct with the contents of the dictionary? Or how to transform it into struct?
struct Blabla {
var one: String
var two: [Int]
init(three: [String: [Int]]) {
for i in three {
self.one = i.key
self.two = i.value
}
} ERROR! - Return from initializer without initializing all stored properties
}
struct Blabla {
var one: String
var two: [Int]
init(three: [String: [Int]]) {
one = ""
two = []
for i in three {
self.one = i.key
self.two = i.value
}
} ERROR! - Return from initializer without initializing all stored properties
}
for in clause may have zero runs, in which case struct properties will not be initialized. You have to provide default values (or emit fatalError if you really need to).
While I think your example is pure synthetical, there is no need to loop through array, you can set properties to its last entry.
The issues is that if three is an empty Dictionary, the instance properties one and two don't get initialised. Also, you are overwriting the properties in each iteration of the for loop and the compiler cannot guarantee that there will be any iterations of the loop in compile-time, hence the compiler error.
You could make the initialiser failable to account for this by checking that the dictionary actually contains at least one key-value pair and assigning that first key-value pair to your properties.
struct Blabla {
var one: String
var two: [Int]
init?(three: [String: [Int]]) {
guard let key = three.keys.first, let value = three[key] else { return nil }
one = key
two = value
}
}
However, you should rethink what it is that you are actually trying to achieve, since with your current setup you have a mismatch between your init input values and the properties of your struct.
This code should compile, but it feels unsafe to me to initialize a Struct in this way because:
It assume your dictionary has values in it.
Your stored properties will always have the last value you looped through.
In order to pull values out to satisfy the compiler you need to force unwrap them. (With Dávid Pásztor's guard-letting approach, this can be avoided)
struct Blabla {
var one: String
var two: [Int]
init(three: [String: [Int]]) {
self.one = three.keys.first!
self.two = three[three.keys.first!]!
}
}
let input = ["pizza": [1,2]]
let l = Blabla(three: input)
If I were you I would let the memberwise initializer that you get for free do its thing and provide either a specialized initializer to handle your case of taking a Dictionary as input or move that parsing to another function/class/etc....
The compiler error is clear: If the dictionary is empty the struct members are never initialized. But the code makes no sense anyway as each iteration of the dictionary overwrites the values.
Maybe you mean to map the dictionary to an array of the struct
struct Blabla {
let one: String
let two: [Int]
}
let three = ["A":[1,2], "B":[3,4]]
let blabla = three.map{Blabla(one: $0.key, two: $0.value)}
print(blabla) // [Blabla(one: "A", two: [1, 2]), Blabla(one: "B", two: [3, 4])]
struct blabla{
var a : string
var b : [int] = []
init(_ data: [string:[int]]){
// whatever you want to do
}
}

How to get the last dictionary element on Swift?

How to get last element on Swift ?
var test: [Int:String] = [Int:String]()
test[1] = "CCC"
test[3] = "AAA"
test[2] = "BBB"
I am trying to get Int 3. I try to use endIndex like this
let (key,value) = test.endIndex
and
print(test.endIndex)
but it does not work.
You haven't been very clear, but I think you're trying to get the last element of an ordered dictionary. Swift's native Dictionary type is unordered, so you will have to manually sort it:
test.keys.sort().last.map({ ($0, test[$0]!) })
In your specific case, the code above will return Optional((3, "AAA")) (i.e. typed Optional<(Key, Value )>).

Optional vs Bound value assigning var from array

I want to check if there is a value in a array and if so assign to a String using a if-left statement:
if let scoreValue = scoreValueArray[element!]{
// do something with scoreValue
}
Error: Bound value in a conditional binding must be of optional type
So tried changing the ! to ? but error persists.
Any input appreciated.
scoreValueArray is an array of strings, where a String value is appended to array if a condition is met, then array is saved to NSUserdefaults.
So element is a int which corresponds to a index in the array, bt only if the index is occupied with a String, so
scoreValueArray[element!]
could return an 'Index out of bounds', hence want to use the if-let.
Although the accepted answer clearly puts why optional binding is not available in the current implementation, it doesn't provide with a solution.
As it is shown in this answer, protocols provide an elegant way of safely checking the bounds of an array. Here's the Swift 2.0 version:
extension Array {
subscript (safe index: Int) -> Element? {
return indices ~= index ? self[index] : nil
}
}
Which you can use like this:
let fruits = ["Apple", "Banana", "Cherry"]
if let fruit = fruits[safe: 4] {
// Do something with the fruit
}
It's not clear what type your scoreValueArray is, but for the sake of this answer, I'm going to assume it's an array of Int.
var scoreValueArray: Array<Int>
Now, if we look the definition of the Array struct, we'll find this:
struct Array<T> : MutableCollectionType, Sliceable {
// other stuff...
subscript (index: Int) -> T
// more stuff
}
So, calling the subscript method on our array (which is what we do when we say scoreValueArray) returns a non-optional. And non-optionals cannot be used in the conditional binding if let/if var statements.
We can duplicate this error message in a more simple example:
let foo: Int = 3
if let bar = foo {
// same error
}
This produces the same error. If we instead do something more like the following, we can avoid the error:
let foo: Int? = 3
if let bar = foo {
// perfectly valid
}
This is different from a dictionary, whose subscript method does return an optional (T?). A dictionary will return a value if the key passed in the subscript is found or nil if there is no value for the passed key.
We must avoid array-index-out-of-bounds exceptions in the same way we always do... by checking the array's length:
if element < scoreValueArray.count {
scoreValue = scoreValueArray[element]
}

Mixing for-in and if-let in Swift

Can I merge a for-in and if-let in one statement?
for item in array {
if let f = item as? NSDictionary {
result.addObject(newFile(f))
}
}
array is made by a JSON, so I don't know if each item is a NSDictionary or not. I have to check.
I was looking for something like this:
for item as? NSDictionary in array {
// code
}
Like Python or Ruby.
#nickfalk is on the right track, but we can do better. His result unfortunately returns [AnyObject], which you can't then call newFile with (I assume). But that's ok, we can get the rest of the way pretty easily.
What you want is partial map. That is to say, you want to map some (but possibly not all) of the elements of one list to another list (from AnyObject to File, if we can). So there must be some rule for choosing, and some rule for mapping. Optional let's us combine those. Let's call the function that does that f. Then its type is:
f: T->U?
So there's some magic function that will possibly convert T to U. We want to map with that. Sounds easy:
extension Array {
func partialMap<U>(f: T->U?) -> [U] {
var result = [U]()
for x in self {
if let u = f(x) {
result.append(u)
}
}
return result
}
}
So now we've hidden all the nasty mutation and var and whatnot down deep where we don't have to look at it. We have a function that takes a mapping function from "something" to "maybe something else" and returns a list of "something elses that we could map."
Now everything is nice and immutable and reusable:
let result = array.partialMap { ($0 as? NSDictionary).map(newFile) }
Whoa there. What's that map in the middle? Well, as? returns NSDictionary?. When you map an optional, then if the optional is None, it returns None, otherwise it applies the function to the value and wraps it in Some. So this whole thing takes AnyObject and returns File? just like we wanted. One partialMap later we have our answer.
I would probably just go for something like:
let result = array.filter() { $0 is NSDictionary }
If you need result to be an NSDictionary array, you can just cast it:
let result = array.filter() { $0 is NSDictionary } as [NSDictionary]
If your goal is to reduce an NSArray to an array only containing NSDictionary filter is a very powerful tool. Create the appropriate filtering function:
func filterForNSDictionary(object: AnyObject) -> Bool{
return object.isKindOfClass(NSDictionary)
}
Then simply pass in you array and function to the filter function
let result = filter(array, filterForNSDictionary)
As #RobNapier points out my solution above will end up with a result array being of the type [AnyObject] this can of course easily be remedied:
let result = filter(array, filterForNSDictionary) as [NSDictionary]
This could be considered risky, if you force the array to be of the wrong type. as [NSString] (for instance9 would most likely blow up in your face down the line...
Rob's solution being pure awesome cleverness of course and #MattGibson delivering the perfect shorthand, while exposing me as an absolute beginner in this field.