How to expand array elements in Swift? (i.e. Array <Int?> as an array <Int>)

Let's say that I have an array of strings and I want to map it to an Int array. I can use the map function:

var arrayOfStrings: Array = ["0", "a"] let numbersOptional = arrayOfStrings.map { $0.toInt() } // numbersOptional = "[Optional(0), nil]" 

The numbers are now an Int? Array, but I need an Int array. I know I can do this:

 let numbers = arrayOfStrings.map { $0.toInt() }.filter { $0 != nil }.map { $0! } // numbers = [0] 

But it does not seem very fast. Convert from Int array? to Array of Int requires calling both a filter and a map with a fairly large number of templates. Is there a faster way to do this?

+6
source share
5 answers

update: Xcode 7.2 β€’ Swift 2.1.1

 let arrayOfStrings = ["0", "a", "1"] let numbersOnly = arrayOfStrings.flatMap { Int($0) } print(numbersOnly) // [0,1] 
+2
source

Update:. Compared to Swift 1.2, there is a built-in flatMap method for arrays, but it does not accept Optional s, so the helper below is still useful.


I like to use the flatMap helper function for things like the Scala flatMap method for collections (which can consider Scala Option as a collection of 0 or 1 elements, roughly):

 func flatMap<C : CollectionType, T>(source: C, transform: (C.Generator.Element) -> T?) -> [T] { var buffer = [T]() for elem in source { if let mappedElem = transform(elem) { buffer.append(mappedElem) } } return buffer } let a = ["0", "a", "42"] let b0 = map(a, { $0.toInt() }) // [Int?] - [{Some 0}, nil, {Some 42}] let b1 = flatMap(a, { $0.toInt() }) // [Int] - [0, 42] 

This definition of flatMap is rather a special case for Optional of what a more general flatMap should do:

 func flatMap<C : CollectionType, T : CollectionType>(source: C, transform: (C.Generator.Element) -> T) -> [T.Generator.Element] { var buffer = [T.Generator.Element]() for elem in source { buffer.extend(transform(elem)) } return buffer } 

where do we get

 let b2 = flatMap(a, { [$0, $0, $0] }) // [String] - ["0", "0", "0", "a", "a", "a", "42", "42", "42"] 
+6
source

Using reduce to create a new array may be more idiomatic

 func filterInt(a: Array<String>) -> Array<Int> { return a.reduce(Array<Int>()) { var a = $0 if let x = $1.toInt() { a.append(x) } return a } } 

Example

 filterInt(["0", "a", "42"]) // [0, 42] 

What you really need is the collect ( map + filter ) method. Given the specific filter you need to apply, even flatMap will work in this case (see Jean-Philippe's answer). Too bad both methods are not provided by the fast library.

+5
source

Theres no good built-in standard Swift library method. However, Haskell has a mapMaybe function that does what you are looking for (assuming you just want to distinguish between null values). Heres the equivalent in Swift:

 func mapSome<S: SequenceType, D: ExtensibleCollectionType> (source: S, transform: (S.Generator.Element)->D.Generator.Element?) -> D { var result = D() for x in source { if let y = transform(x) { result.append(y) } } return result } // version that defaults to returning an array if unspecified func mapSome<S: SequenceType, T> (source: S, transform: (S.Generator.Element)->T?) -> [T] { return mapSome(source, transform) } let s = ["1","2","elephant"] mapSome(s) { $0.toInt() } // [1,2] 
+2
source

You can use reduce , it is more flexible:

 var arrayOfStrings: Array = ["0", "a"] let numbersOptional = arrayOfStrings.reduce([Int]()) { acc, str in if let i = str.toInt() { return acc + [i] } return acc } 
+1
source

Source: https://habr.com/ru/post/973430/


All Articles