Map or abbreviation using an index in Swift

Is there a way to get the index of an array in map or reduce in Swift? I am looking for something like each_with_index in Ruby.

 func lunhCheck(number : String) -> Bool { var odd = true; return reverse(number).map { String($0).toInt()! }.reduce(0) { odd = !odd return $0 + (odd ? ($1 == 9 ? 9 : ($1 * 2) % 9) : $1) } % 10 == 0 } lunhCheck("49927398716") lunhCheck("49927398717") 

I would like to get rid of the odd variable above .

+68
functional-programming swift
Jan 18 '15 at 16:46
source share
5 answers

You can use enumerate to convert a sequence ( Array , String , etc.) into a sequence of tuples using an integer counter, and the element together. I.e:

 let numbers = [7, 8, 9, 10] let indexAndNum: [String] = numbers.enumerate().map { (index, element) in return "\(index): \(element)" } print(indexAndNum) // ["0: 7", "1: 8", "2: 9", "3: 10"] 

Link to enumerate definition

Note that this is not the same as getting the collection index - enumerate returns you an integer counter. This is the same as the index for the array, but a string or dictionary will not be very useful. To get the actual index along with each element, you can use zip :

 let actualIndexAndNum: [String] = zip(numbers.indices, numbers).map { "\($0): \($1)" } print(actualIndexAndNum) // ["0: 7", "1: 8", "2: 9", "3: 10"] 

When using an enumerated sequence with reduce you cannot separate the index and the element in the tuple, since you already have the cumulative / current tuple in the method signature. Instead, you will need to use .0 and .1 for the second parameter to close reduce :

 let summedProducts = numbers.enumerate().reduce(0) { (accumulate, current) in return accumulate + current.0 * current.1 // ^ ^ // index element } print(summedProducts) // 56 

Swift 3.0

Since the syntax of Swift 3.0 is completely different.
Alternatively, you can use short-syntax / inline to display an array in a dictionary:

 let numbers = [7, 8, 9, 10] let array: [(Int, Int)] = numbers.enumerated().map { ($0, $1) } // ^ ^ // index element 

It produces:

 [(0, 7), (1, 8), (2, 9), (3, 10)] 
+150
Jan 18 '15 at 16:55
source share
β€” -

For Swift 2.1 I wrote the following function:

 extension Array { public func mapWithIndex<T> (f: (Int, Element) -> T) -> [T] { return zip((self.startIndex ..< self.endIndex), self).map(f) } } 

And then use it like this:

  let numbers = [7, 8, 9, 10] let numbersWithIndex: [String] = numbers.mapWithIndex { (index, number) -> String in return "\(index): \(number)" } print("Numbers: \(numbersWithIndex)") 
+7
Oct 28 '15 at 17:10
source share

With Swift 3, when you have an object that conforms to the Sequence protocol, and you want to associate each element within it with its index, you can use enumerated() .

For example:

 let array = [1, 18, 32, 7] let enumerateSequence = array.enumerated() // type: EnumerateSequence<[Int]> let newArray = Array(enumerateSequence) print(newArray) // prints: [(0, 1), (1, 18), (2, 32), (3, 7)] 
 let reverseRandomAccessCollection = [1, 18, 32, 7].reversed() let enumerateSequence = reverseRandomAccessCollection.enumerated() // type: EnumerateSequence<ReverseRandomAccessCollection<[Int]>> let newArray = Array(enumerateSequence) print(newArray) // prints: [(0, 7), (1, 32), (2, 18), (3, 1)] 
 let reverseCollection = "8763".characters.reversed() let enumerateSequence = reverseCollection.enumerated() // type: EnumerateSequence<ReverseCollection<String.CharacterView>> let newArray = enumerateSequence.map { ($0.0 + 1, String($0.1) + "A") } print(newArray) // prints: [(1, "3A"), (2, "6A"), (3, "7A"), (4, "8A")] 



Therefore, in the simplest case, you can implement the Moon algorithm on a playground, for example:

 let array = [8, 7, 6, 3] let reversedArray = array.reversed() let enumerateSequence = reversedArray.enumerated() let luhnClosure = { (sum: Int, tuple: (index: Int, value: Int)) -> Int in let indexIsOdd = tuple.index % 2 == 1 guard indexIsOdd else { return sum + tuple.value } let newValue = tuple.value == 9 ? 9 : tuple.value * 2 % 9 return sum + newValue } let sum = enumerateSequence.reduce(0, luhnClosure) let bool = sum % 10 == 0 print(bool) // prints: true 



If you start with String , you can implement it like this:

 let characterView = "8763".characters let mappedArray = characterView.flatMap { Int(String($0)) } let reversedArray = mappedArray.reversed() let enumerateSequence = reversedArray.enumerated() let luhnClosure = { (sum: Int, tuple: (index: Int, value: Int)) -> Int in let indexIsOdd = tuple.index % 2 == 1 guard indexIsOdd else { return sum + tuple.value } let newValue = tuple.value == 9 ? 9 : tuple.value * 2 % 9 return sum + newValue } let sum = enumerateSequence.reduce(0, luhnClosure) let bool = sum % 10 == 0 print(bool) // prints: true 



If you need to repeat these operations, you can reorganize your code into an extension:

 extension String { func luhnCheck() -> Bool { let characterView = self.characters let mappedArray = characterView.flatMap { Int(String($0)) } let reversedArray = mappedArray.reversed() let enumerateSequence = reversedArray.enumerated() let luhnClosure = { (sum: Int, tuple: (index: Int, value: Int)) -> Int in let indexIsOdd = tuple.index % 2 == 1 guard indexIsOdd else { return sum + tuple.value } let newValue = tuple.value == 9 ? 9 : tuple.value * 2 % 9 return sum + newValue } let sum = enumerateSequence.reduce(0, luhnClosure) return sum % 10 == 0 } } let string = "8763" let luhnBool = string.luhnCheck() print(luhnBool) // prints: true 

Or, in short form:

 extension String { func luhnCheck() -> Bool { let sum = characters .flatMap { Int(String($0)) } .reversed() .enumerated() .reduce(0) { let indexIsOdd = $1.0 % 2 == 1 guard indexIsOdd else { return $0 + $1.1 } return $0 + ($1.1 == 9 ? 9 : $1.1 * 2 % 9) } return sum % 10 == 0 } } let string = "8763" let luhnBool = string.luhnCheck() print(luhnBool) // prints: true 
+5
Dec 28 '15 at 23:58
source share

In addition to the Nate Cook map example, you can also apply this behavior to reduce .

 let numbers = [1,2,3,4,5] let indexedNumbers = reduce(numbers, [:]) { (memo, enumerated) -> [Int: Int] in return memo[enumerated.index] = enumerated.element } // [0: 1, 1: 2, 2: 3, 3: 4, 4: 5] 

Note that an EnumerateSequence passed to the closure as enumerated cannot be expanded in a nested manner, so tuple members must be expanded inside the closure (i.e. enumerated.index ).

+2
Jul 14 '15 at 5:01
source share

This is a working CollectionType extension for quick 2.1 using throws and reps:

 extension CollectionType { func map<T>(@noescape transform: (Self.Index, Self.Generator.Element) throws -> T) rethrows -> [T] { return try zip((self.startIndex ..< self.endIndex), self).map(transform) } } 

I know that this is not what you asked for, but it solves your problem. You can try this quick moon 2.0 method without extending anything:

 func luhn(string: String) -> Bool { var sum = 0 for (idx, value) in string.characters.reverse().map( { Int(String($0))! }).enumerate() { sum += ((idx % 2 == 1) ? (value == 9 ? 9 : (value * 2) % 9) : value) } return sum > 0 ? sum % 10 == 0 : false } 
+2
Oct 30 '15 at 13:46
source share



All Articles