Bản đồ hoặc giảm với chỉ mục trong Swift


143

Có cách nào để lấy chỉ mục của mảng trong maphoặc reducetrong Swift không? Tôi đang tìm kiếm một cái gì đó giống như each_with_indextrong 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")

Tôi muốn thoát khỏi các oddbiến ở trên .

Câu trả lời:


313

Bạn có thể sử dụng enumerateđể chuyển đổi một chuỗi ( Array,, Stringv.v.) thành một chuỗi các bộ dữ liệu với bộ đếm số nguyên và phần tử được ghép với nhau. Đó là:

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

Liên kết đến enumerateđịnh nghĩa

Lưu ý rằng điều này không giống với việc lấy chỉ mục của bộ sưu tập enumeratemà Google cung cấp cho bạn bộ đếm số nguyên. Đây là giống như chỉ mục cho một mảng, nhưng trên một chuỗi hoặc từ điển sẽ không hữu ích. Để có được chỉ mục thực tế cùng với từng yếu tố, bạn có thể sử dụng zip:

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

Khi sử dụng một chuỗi liệt kê với reduce, bạn sẽ không thể tách chỉ mục và phần tử trong một tuple, vì bạn đã có bộ dữ liệu tích lũy / hiện tại trong chữ ký phương thức. Thay vào đó, bạn sẽ cần sử dụng .0.1trên tham số thứ hai để reduceđóng:

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

Swift 3.0 trở lên

Vì cú pháp Swift 3.0 khá khác nhau.
Ngoài ra, bạn có thể sử dụng cú pháp ngắn / nội tuyến để ánh xạ mảng trên từ điển:

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

Điều đó tạo ra:

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

1
Không muốn đánh cắp niềm vui của việc hoàn thiện, vì vậy nếu bạn cần, tôi đặt kiểm tra Luhn đã sửa đổi trong một ý chính thay vì trong câu trả lời: gist.github.com/natecook1000/1eb756d6b10297006137
Nate Cook

5
Trong swift 2.0 bạn cần làm:numbers.enumerate().map { (index, element) in ...
Robert

@CharlieMartin: Bạn có thể sử dụng .reducesau enumerate()hoặc zip.
Nate Cook

Nhưng với một chỉ số? Tôi nhận được một lỗi rằng hàm chỉ yêu cầu hai tham số và giảm lấy đối tượng inital (kết quả của việc giảm) làm tham số đầu tiên và giá trị hiện tại được lặp lại là tham số thứ hai. Hiện tại tôi chỉ đang sử dụng một for..in
Charlie Martin

Trong Swift 5 enumeratelà bây giờenumerated
lustig

10

Đối với Swift 2.1tôi đã viết chức năng tiếp theo:

extension Array {

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

Và sau đó sử dụng nó như thế này:

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

8

Với Swift 3, khi bạn có một đối tượng phù hợp với Sequencegiao thức và bạn muốn liên kết từng phần tử bên trong nó với chỉ mục của nó, bạn có thể sử dụng enumerated()phương thức.

Ví dụ:

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

Do đó, trong trường hợp đơn giản nhất, bạn có thể triển khai thuật toán Luhn trong Sân chơi như sau:

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

Nếu bạn bắt đầu từ a String, bạn có thể thực hiện nó như thế này:

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

Nếu bạn cần lặp lại các thao tác đó, bạn có thể cấu trúc lại mã của mình thành một phần mở rộng:

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

Hoặc, theo một cách ngắn gọn:

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

Ngoài ví dụ về Nate Cook map, bạn cũng có thể áp dụng hành vi này cho 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]

Lưu ý rằng việc EnumerateSequencetruyền vào bao đóng là enumeratedkhông thể phân tách theo kiểu lồng nhau, do đó các thành viên của bộ dữ liệu phải được phân tách bên trong bao đóng (ví dụ. enumerated.index).


2

Đây là một phần mở rộng CollectionType hoạt động cho swift 2.1 bằng cách sử dụng các lần ném và rút lui:

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

}

Tôi biết đây không phải là những gì bạn đã hỏi, nhưng giải quyết vấn đề của bạn. Bạn có thể thử phương pháp Luhn 2.0 nhanh chóng này mà không cần mở rộng bất cứ điều gì:

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
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.