Zuordnen oder Reduzieren mit Index in Swift

143

Gibt es eine Möglichkeit, den Index des Arrays in mapoder reducein Swift abzurufen? Ich suche so etwas wie each_with_indexin 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")

Ich möchte die obigeodd Variable loswerden .

Jonas Elfström
quelle

Antworten:

312

Sie können enumerateeine Sequenz ( usw.) in eine Sequenz von Tupeln konvertieren Array, Stringwobei ein ganzzahliger Zähler und ein Element miteinander gepaart sind. Das ist:

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 zu enumerate Definition

Beachten Sie, dass dies nicht mit dem Abrufen des Index der Sammlung identisch ist. enumerateSie erhalten einen ganzzahligen Zähler zurück. Dies entspricht dem Index für ein Array, ist jedoch für eine Zeichenfolge oder ein Wörterbuch nicht sehr nützlich. Um den tatsächlichen Index zusammen mit jedem Element zu erhalten, können Sie Folgendes verwenden zip:

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

Wenn Sie eine Aufzählungssequenz mit verwenden reduce, können Sie den Index und das Element in einem Tupel nicht trennen, da Sie das akkumulierende / aktuelle Tupel bereits in der Methodensignatur haben. Stattdessen müssen Sie verwenden .0und .1auf dem zweiten Parameter zu Ihrer reduceSchließung:

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

Swift 3.0 und höher

Da Swift 3.0 Syntax ist ganz anders.
Sie können auch Short-Syntax / Inline verwenden, um das Array im Wörterbuch abzubilden:

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

Das ergibt:

[(0, 7), (1, 8), (2, 9), (3, 10)]
Nate Cook
quelle
1
Ich möchte nicht den Spaß am Finishing stehlen, also wenn ich ihn brauche, setze ich den modifizierten Luhn-Scheck in eine Liste anstatt in die Antwort: gist.github.com/natecook1000/1eb756d6b10297006137
Nate Cook
5
In Swift 2.0 müssen Sie tun:numbers.enumerate().map { (index, element) in ...
Robert
@CharlieMartin: Sie können .reducenach enumerate()oder verwenden zip.
Nate Cook
Aber mit einem Index? Ich erhalte die Fehlermeldung, dass die Funktion nur zwei Parameter benötigt und reduziere das ursprüngliche Objekt (das Ergebnis der Reduktion) als ersten Parameter und den aktuellen Wert als zweiten. Ich benutze derzeit nur ein for..in stattdessen
Charlie Martin
In Swift 5 enumerateist jetztenumerated
lustig
10

Denn Swift 2.1ich habe nächste Funktion geschrieben:

extension Array {

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

Und dann benutze es so:

    let numbers = [7, 8, 9, 10]
    let numbersWithIndex: [String] = numbers.mapWithIndex { (index, number) -> String in
        return "\(index): \(number)" 
    }
    print("Numbers: \(numbersWithIndex)")
Oleksandr Karaberov
quelle
8

Wenn Sie mit Swift 3 ein Sequenceprotokollkonformes Objekt haben und jedes Element darin mit seinem Index verknüpfen möchten, können Sie die enumerated()Methode verwenden.

Beispielsweise:

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")]

Daher können Sie im einfachsten Fall einen Luhn-Algorithmus auf einem Spielplatz wie folgt implementieren:

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

Wenn Sie von a ausgehen String, können Sie es folgendermaßen implementieren:

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

Wenn Sie diese Vorgänge wiederholen müssen, können Sie Ihren Code in eine Erweiterung umgestalten:

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

Oder sehr prägnant:

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
Imanou Petit
quelle
2

Zusätzlich zu Nate Cooks Beispiel von mapkönnen Sie dieses Verhalten auch auf anwenden 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]

Es ist zu beachten, dass die EnumerateSequencein den Verschluss übergebenen enumeratedElemente nicht verschachtelt zerlegt werden können, daher müssen die Elemente des Tupels innerhalb des Verschlusses zerlegt werden (dh enumerated.index).

Levi McCallum
quelle
2

Dies ist eine funktionierende CollectionType- Erweiterung für Swift 2.1 mit Würfen und erneuten Würfen:

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)
    }

}

Ich weiß, das haben Sie nicht gefragt, aber es löst Ihr Problem. Sie können diese schnelle 2.0 Luhn-Methode ausprobieren, ohne etwas zu erweitern:

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
}
Peter Stajger
quelle