แผนที่หรือลดด้วยดัชนีใน Swift


143

มีวิธีรับดัชนีของอาร์เรย์ในmapหรือreduceใน Swift หรือไม่ ฉันกำลังมองหาบางอย่างเช่นeach_with_indexในทับทิม

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

ฉันต้องการที่จะได้รับการกำจัดของoddตัวแปรดังกล่าวข้างต้น

คำตอบ:


314

คุณสามารถใช้enumerateในการแปลงลำดับ ( Array, Stringฯลฯ ) เป็นลำดับของ tuples ด้วยตัวนับจำนวนเต็มและองค์ประกอบที่จับคู่กัน นั่นคือ:

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

ลิงก์ไปที่enumerateคำจำกัดความ

โปรดทราบว่าสิ่งนี้ไม่เหมือนกับการรับดัชนีของคอลเล็กชัน - enumerateให้คุณนับจำนวนเต็ม สิ่งนี้เหมือนกับดัชนีของอาร์เรย์ แต่ในสตริงหรือพจนานุกรมจะไม่มีประโยชน์มาก ในการรับดัชนีจริงพร้อมกับแต่ละองค์ประกอบคุณสามารถใช้zip:

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

เมื่อใช้ลำดับที่แจกแจงด้วยreduceคุณจะไม่สามารถแยกดัชนีและองค์ประกอบใน tuple เนื่องจากคุณมี tuple ที่สะสม / ปัจจุบันในลายเซ็นวิธีการ แต่คุณจะต้องใช้.0และ.1ในพารามิเตอร์ที่สองเพื่อreduceปิดของคุณ:

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

Swift 3.0 ขึ้นไป

เนื่องจากไวยากรณ์ของ Swift 3.0 แตกต่างกันมาก
นอกจากนี้คุณสามารถใช้ไวยากรณ์สั้น / อินไลน์เพื่อจับคู่อาร์เรย์กับพจนานุกรม:

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

ที่ผลิต:

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

1
ไม่ต้องการที่จะขโมยความสนุกของการตกแต่งดังนั้นถ้าคุณต้องการฉันใส่การตรวจสอบ Luhn แก้ไขในส่วนสำคัญแทนในคำตอบ: gist.github.com/natecook1000/1eb756d6b10297006137
เนท Cook

5
ใน swift 2.0 คุณต้องทำ:numbers.enumerate().map { (index, element) in ...
Robert

@CharlieMartin: คุณสามารถใช้.reduceหลังหรือenumerate() zip
Nate Cook

แต่มีดัชนี? ฉันได้รับข้อผิดพลาดว่าฟังก์ชั่นต้องการเพียงสองพารามิเตอร์และลดใช้วัตถุ inital (ผลของการลด) เป็นพารามิเตอร์แรกและค่าปัจจุบันถูกทำซ้ำเป็นครั้งที่สอง ตอนนี้ฉันเพิ่งใช้ for..in แทน
Charlie Martin

ใน Swift 5 enumerateตอนนี้enumerated
lustig

10

สำหรับSwift 2.1ฉันเขียนฟังก์ชั่นต่อไป:

extension Array {

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

แล้วใช้มันเช่นนี้

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

8

ด้วย Swift 3 เมื่อคุณมีวัตถุที่สอดคล้องกับSequenceโปรโตคอลและคุณต้องการเชื่อมโยงแต่ละองค์ประกอบภายในของมันกับดัชนีคุณสามารถใช้enumerated()วิธีการ

ตัวอย่างเช่น:

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

ดังนั้นในกรณีที่ง่ายที่สุดคุณสามารถใช้อัลกอริทึม Luhn ในสนามเด็กเล่นเช่นนี้:

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

หากคุณเริ่มต้นจาก a Stringคุณสามารถใช้มันได้ดังนี้:

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

หากคุณต้องการทำซ้ำการดำเนินการเหล่านั้นคุณสามารถ refactor รหัสของคุณเป็นส่วนขยาย:

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

หรือในทางที่รัดกุมมาก:

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

นอกจากตัวอย่างเนทของแม่ครัวของคุณยังสามารถนำไปใช้ในการทำงานนี้mapreduce

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]

โปรดทราบว่าการEnumerateSequenceส่งผ่านไปยังการปิดที่enumeratedไม่สามารถย่อยสลายในแบบซ้อนดังนั้นสมาชิกของ tuple จะต้องสลายตัวภายในการปิด (เช่น. enumerated.index)


2

นี่เป็นส่วนขยายCollectionType ที่ใช้งานได้สำหรับ swift 2.1 โดยใช้การโยนและการทำใหม่:

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

}

ฉันรู้ว่านี่ไม่ใช่สิ่งที่คุณขอ แต่แก้ไขปัญหาของคุณ คุณสามารถลองใช้วิธีนี้อย่างรวดเร็ว 2.0 Luhn โดยไม่ต้องขยายอะไร:

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
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.