0

I am reading a text file containing key/value lines into Swift structs. One of the lines of the file has five space-separated values in the line, which I've turned into an array using componentsSeparatedByString.

The actual values are Ints, so the normal solution would be to loop over each one and copy it into the associated slot in my struct's Int array. But am I missing some Swift (or Foundation) magic here?

1 Answer 1

2

You can use the map function, which loops through all the elements of the array and apply a transformation to each element defined in a closure you pass to it.

var array = ["2", "4", "6", "8"]

let ints = array.map { $0.toInt()! }

Note that I am using forced unwrapping when converting to integer - so use it if you are 100% sure all elements in the array are actually integers, otherwise a runtime exception will be generated

A safer way is to map to optional integer, then filter to remove nil values, and then map again to force unwrap optionals:

let ints = array.map { $0.toInt() }.filter { $0 != nil }.map { $0! }

Note that this safer version may seem slower than using a for loop (and actually it is, because it traverses the array 3 times - unless the compiler is able to optimize it) - but I would prefer it over a loop because it's more compact and in my opinion more readable. Needless to say, I wouldn't probably use for large arrays though.

Addendum: as suggested by @MikeS, it's possible to use reduce to combine the last 2 steps of the safer version:

let ints = array.map { $0.toInt() }.reduce([]) { $1 != nil ? $0 + [$1!] : $0 }

It looks like a good alternative for small sized arrays, because it reduces the complexity from O(3n) to O(2n) - although I suspect that without compiler optimizations it might be slower because at each iteration a new array is created (if the element is not nil), because of $0 + [$1!].

But it's good to know that there are many ways to achieve the same result :)

Sign up to request clarification or add additional context in comments.

6 Comments

And that is why I asked. Very useful!
@Antonio you can remove one of those loops by using reduce instead of filter and map to remove the nils: let ints = array.map { $0.toInt() }.reduce([]) { $1 != nil ? $0 + [$1!] : $0 }. You could also use reduce to do the initial mapping, but then you're basically back to a for loop and I can't think of a way to do it as a one-liner.
Thanks @MikeS - added as an alternative solution - let me know if you agree with my considerations
@Antonio yeah, I agree that the extra array creation on each iteration could slow things down; I couldn't find a way to append a single element to an immutable array. Overall, I think your map, filter, map solution is probably better (for small arrays of course), if for no other reason than readability.
@MikeS but your solution might work better if the number of nil elements is larger than the non-nil, especially for basic data types (Int, Float, etc.).
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.