Swift'teki dizinle eşleme veya küçültme


143

Swift'in içinde mapveya reduceiçinde dizinin dizinini almanın bir yolu var mı ? each_with_indexRuby gibi bir şey arıyorum .

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

Yukarıdakiodd değişkenten kurtulmak istiyorum .

Yanıtlar:


314

enumerateBir diziyi ( Array, Stringvb.) Bir tamsayı sayacı ve ve öğenin eşleştirilmiş olduğu bir dizi tuple dönüştürmek için kullanabilirsiniz . Yani:

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

Bağlantı enumeratetanımı

Bunun koleksiyonun dizinini almakla aynı olmadığını unutmayın; enumeratesize bir tamsayı sayacı verir. Bu, bir dizinin diziniyle aynıdır, ancak bir dizede veya sözlükte çok yararlı olmaz. Her bir öğeyle birlikte gerçek dizini almak için şunları kullanabilirsiniz zip:

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

İle numaralandırılmış bir diziyi kullanırken reduce, yöntem imzasında zaten biriken / akım dizisine sahip olduğunuzdan , dizini ve öğeyi bir demet içinde ayıramazsınız. Bunun yerine, kapatmanız için ikinci parametrede .0ve kullanmanız gerekir :.1reduce

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

Swift 3.0 ve üstü

Swift 3.0 sözdizimi oldukça farklı olduğundan.
Ayrıca, sözlükteki diziyi eşlemek için kısa sözdizimi / satır içi kullanabilirsiniz:

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

Üreten:

[(0, 7), (1, 8), (2, 9), (3, 10)]

1
Bitirme eğlencesini çalmak istemiyorum, bu yüzden ihtiyacınız varsa değiştirilmiş Luhn kontrolünü cevap yerine bir öze koydum: gist.github.com/natecook1000/1eb756d6b10297006137
Nate Cook

5
Hızlı 2.0'da yapmanız gerekenler:numbers.enumerate().map { (index, element) in ...
Robert

@CharlieMartin: veya .reducesonrasında kullanabilirsiniz . enumerate()zip
Nate Cook

Ama bir endeks ile mi? Ben fonksiyonu sadece iki parametre gerektirir bir hata alıyorum ve azaltma ilk param olarak inital nesne (azaltmanın sonucu) alır ve geçerli değer ikinci olarak yineleniyor. Şu anda sadece for..in kullanıyorum
Charlie Martin

Swift 5 enumerateşimdienumerated
lustig

10

Çünkü bir Swift 2.1sonraki işlevi yazdım:

extension Array {

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

Ve sonra böyle kullanın:

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

8

Swift 3 ile, Sequenceprotokole uyan bir nesneniz olduğunda ve içindeki her bir öğeyi diziniyle bağlamak istediğinizde,enumerated() yöntemi .

Örneğin:

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

Bu nedenle, en basit durumda, bir Oyun Alanında böyle bir Luhn algoritması uygulayabilirsiniz:

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

Birinden başlarsanız String, bunu şu şekilde uygulayabilirsiniz:

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

Bu işlemleri tekrarlamanız gerekirse, kodunuzu bir uzantıya yeniden aktarabilirsiniz:

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

Veya çok özlü bir şekilde:

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

2

Nate Cook'un örneğine ek olarak map, bu davranışı da uygulayabilirsiniz 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]

O Not EnumerateSequenceolarak kapatılması geçirilen enumeratediç içe geçmiş bir şekilde temin edilememiştir, böylece başlığın üyeleri kapatılması içine çürümüş edilmelidir (yani. enumerated.index).


2

Bu, atar ve tekrarlar kullanarak hızlı 2.1 için çalışan bir CollectionType uzantısıdır:

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

}

Bunun istediğini olmadığını biliyorum, ama sorununu çözdü. Bu hızlı 2.0 Luhn yöntemini hiçbir şey uzatmadan deneyebilirsiniz:

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
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.