Swift'te dizin ve öğe içeren bir döngü nasıl yinelenir


784

Bir dizi üzerinden yineleme ve Python enumerate gibi hem dizin hem de öğe var kullanabileceğiniz bir işlevi var mı?

for index, element in enumerate(list):
    ...

Yanıtlar:


1660

Evet. Swift 3.0'dan itibaren, her öğenin değeriyle birlikte dizine ihtiyacınız varsa , diziyi yinelemek için enumerated()yöntemi kullanabilirsiniz . Dizideki her öğe için dizinden ve değerden oluşan bir çift dizi döndürür. Örneğin:

for (index, element) in list.enumerated() {
  print("Item \(index): \(element)")
}

Swift 3.0'dan önce ve Swift 2.0'dan sonra, fonksiyon çağrıldı enumerate():

for (index, element) in list.enumerate() {
    print("Item \(index): \(element)")
}

Swift 2.0'dan önce enumerateküresel bir işlev vardı.

for (index, element) in enumerate(list) {
    println("Item \(index): \(element)")
}

3
Bir demet gibi görünse de, Swift 1.2'de - yaklaşık 2.0'dan emin değilim - bir EnumerateSequence <base: SequenceType> yapısı döndürür.
nstein

2
@Leviathlon farkedilebilir veya ölçülebilir performans yükü önemli olacak mı? Hayır.
Dan Rosenstark

kullanmanın tek yararı dizine erişim sağlamak enumeratemı?
Honey

29
Belki enumeratingSwift 4 için uçağa geçecekler . Heyecan verici!
Dan Rosenstark

3
@Honey for (index, element) inkullanırken enumeratedyanıltıcıdır. olmalıfor (offset, element) in
Leo Dabus

115

Swift 5 adı verilen bir yöntem sağlar enumerated()için Array. enumerated()aşağıdaki beyanı içerir:

func enumerated() -> EnumeratedSequence<Array<Element>>

Bir çift dizisini (n, x) döndürür; burada n, sıfırdan başlayarak ardışık bir tamsayıyı temsil eder ve x, dizinin bir öğesini temsil eder.


En basit durumlarda, enumerated()for döngüsü ile kullanabilirsiniz . Örneğin:

let list = ["Car", "Bike", "Plane", "Boat"]
for (index, element) in list.enumerated() {
    print(index, ":", element)
}

/*
prints:
0 : Car
1 : Bike
2 : Plane
3 : Boat
*/

Bununla enumerated()birlikte, for döngüsü ile kullanmakla sınırlı olmadığınızı unutmayın . Aslında, enumerated()aşağıdaki koda benzer bir şey için bir for döngüsü ile kullanmayı planlıyorsanız , yanlış yapıyorsunuz:

let list = [Int](1...5)
var arrayOfTuples = [(Int, Int)]()

for (index, element) in list.enumerated() {
    arrayOfTuples += [(index, element)]
}

print(arrayOfTuples) // prints [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5)]

Bunu yapmanın en hızlı yolu:

let list = [Int](1...5)
let arrayOfTuples = Array(list.enumerated())
print(arrayOfTuples) // prints [(offset: 0, element: 1), (offset: 1, element: 2), (offset: 2, element: 3), (offset: 3, element: 4), (offset: 4, element: 5)]

Alternatif olarak, aynı zamanda kullanabilir enumerated()ile map:

let list = [Int](1...5)
let arrayOfDictionaries = list.enumerated().map { (a, b) in return [a : b] }
print(arrayOfDictionaries) // prints [[0: 1], [1: 2], [2: 3], [3: 4], [4: 5]]

Bazı olmasına rağmen Dahası, sınırlamaları , forEachdöngü için iyi bir yedek olabilir:

let list = [Int](1...5)
list.reversed().enumerated().forEach { print($0, ":", $1) }

/*
prints:
0 : 5
1 : 4
2 : 3
3 : 2
4 : 1
*/

Kullanarak enumerated()ve makeIterator(), hatta sizinle ilgili şu elle yineleme yapabilirsiniz Array. Örneğin:

import UIKit
import PlaygroundSupport

class ViewController: UIViewController {

    var generator = ["Car", "Bike", "Plane", "Boat"].enumerated().makeIterator()

    override func viewDidLoad() {
        super.viewDidLoad()

        let button = UIButton(type: .system)
        button.setTitle("Tap", for: .normal)
        button.frame = CGRect(x: 100, y: 100, width: 100, height: 100)
        button.addTarget(self, action: #selector(iterate(_:)), for: .touchUpInside)
        view.addSubview(button)
    }

    @objc func iterate(_ sender: UIButton) {
        let tuple = generator.next()
        print(String(describing: tuple))
    }

}

PlaygroundPage.current.liveView = ViewController()

/*
 Optional((offset: 0, element: "Car"))
 Optional((offset: 1, element: "Bike"))
 Optional((offset: 2, element: "Plane"))
 Optional((offset: 3, element: "Boat"))
 nil
 nil
 nil
 */

1
Endekse erişim elde etmenin tek faydası var enumeratemı?
Bal

52

Swift 2'den başlayarak, numaralandırma işlevinin koleksiyonda şu şekilde çağrılması gerekir:

for (index, element) in list.enumerate() {
    print("Item \(index): \(element)")
}

47

Bu cevabı bir Sözlük ile yapmanın bir yolunu ararken buldum ve bunu uyarlamak oldukça kolay, sadece öğe için bir demet geçirin.

// Swift 2

var list = ["a": 1, "b": 2]

for (index, (letter, value)) in list.enumerate() {
    print("Item \(index): \(letter) \(value)")
}

18

İstediğiniz sonucu elde etmek için numaralandırma döngüsünü kullanabilirsiniz:

Hızlı 2:

for (index, element) in elements.enumerate() {
    print("\(index): \(element)")
}

Hızlı 3 ve 4:

for (index, element) in elements.enumerated() {
    print("\(index): \(element)")
}

Ya da aynı sonucu elde etmek için bir for döngüsünden geçebilirsiniz:

for index in 0..<elements.count {
    let element = elements[index]
    print("\(index): \(element)")
}

Umarım yardımcı olur.


14

Temel numaralandırma

for (index, element) in arrayOfValues.enumerate() {
// do something useful
}

veya Swift 3 ile ...

for (index, element) in arrayOfValues.enumerated() {
// do something useful
}

Numaralandırma, Filtreleme ve Haritalama

Ancak, enumerate öğesini harita veya filtreyle birlikte kullanıyorum. Örneğin, birkaç dizide çalışırken.

Bu dizide tek veya hatta dizine alınmış öğeleri filtrelemek ve bunları Ints'ten Çiftler'e dönüştürmek istedim. Böylece enumerate()indeksi ve elemanı alır, daha sonra filtre indeksi kontrol eder ve son olarak ortaya çıkan demetten kurtulmak için onu sadece elemanla eşleştiririm.

let evens = arrayOfValues.enumerate().filter({
                            (index: Int, element: Int) -> Bool in
                            return index % 2 == 0
                        }).map({ (_: Int, element: Int) -> Double in
                            return Double(element)
                        })
let odds = arrayOfValues.enumerate().filter({
                            (index: Int, element: Int) -> Bool in
                            return index % 2 != 0
                        }).map({ (_: Int, element: Int) -> Double in
                            return Double(element)
                        })

9

.enumerate()Çalışmaları kullanmak , ancak öğenin gerçek dizinini sağlamaz; birbirini izleyen her öğe için yalnızca 0 ile başlayan ve 1 ile artan bir Int sağlar. Bu genellikle ilgisizdir, ancak türle birlikte kullanıldığında beklenmedik davranış potansiyeli vardır ArraySlice. Aşağıdaki kodu alın:

let a = ["a", "b", "c", "d", "e"]
a.indices //=> 0..<5

let aSlice = a[1..<4] //=> ArraySlice with ["b", "c", "d"]
aSlice.indices //=> 1..<4

var test = [Int: String]()
for (index, element) in aSlice.enumerate() {
    test[index] = element
}
test //=> [0: "b", 1: "c", 2: "d"] // indices presented as 0..<3, but they are actually 1..<4
test[0] == aSlice[0] // ERROR: out of bounds

Bu biraz çelişkili bir örnek ve pratikte yaygın bir sorun değil ama yine de bunun olabileceğini bilmeye değer olduğunu düşünüyorum.


2
it does not actually provide the true index of the element; it only provides an Int beginning with 0 and incrementing by 1 for each successive elementEvet, bu yüzden numaralandırma denir . Ayrıca, dilim dizi değildir, bu yüzden sürpriz farklı davranır. Burada bir hata yok - her şey tasarım gereğidir. :)
Eric Aya

5
Doğru, ama ben asla bir hata demedim. ArraySlice tipiyle nasıl olumsuz etkileşime girebileceğini bilmeyenler için bahsetmeye değer olduğunu düşündüğüm beklenmedik bir davranış.
Cole Campbell

Asıl öğenin dizinini almanın herhangi bir yolunun farkında mısınız - örneğin, filterilk kullanıyorsanız ?
Alexandre Cassagne

9

Swift 3'ten başlayarak,

for (index, element) in list.enumerated() {
  print("Item \(index): \(element)")
}

9

Tamlık için dizi dizinleriniz üzerinde yineleme yapabilir ve ilgili dizindeki öğeye erişmek için alt simge kullanabilirsiniz:

let list = [100,200,300,400,500]
for index in list.indices {
    print("Element at:", index, " Value:", list[index])
}

ForEach kullanımı

list.indices.forEach {
    print("Element at:", $0, " Value:", list[$0])
}

Toplama enumerated()yöntemini kullanma . Not onunla küpe koleksiyonu döndüren offsetve element:

for item in list.enumerated() {
    print("Element at:", item.offset, " Value:", item.element)
}

forEach kullanarak:

list.enumerated().forEach {
    print("Element at:", $0.offset, " Value:", $0.element)
}

Bunlar yazdıracak

Eleman: 0 Değer: 100

Element değeri: 1 Değer: 200

Element değeri: 2 Değer: 300

Element değeri: 3 Değer: 400

Elemanda: 4 Değer: 500

Dizi dizinine (ofset değil) ve elemanına ihtiyacınız varsa, Koleksiyonu genişletebilir ve dizinlenmiş öğeleri almak için kendi yönteminizi oluşturabilirsiniz:

extension Collection {
    func indexedElements(body: ((index: Index, element: Element)) throws -> Void) rethrows {
        var index = startIndex
        for element in self {
            try body((index,element))
            formIndex(after: &index)
        }
    }
}

Alex'in önerdiği bir başka olası uygulama, toplama endekslerini unsurlarıyla sıkıştırmaktır:

extension Collection {
    func indexedElements(body: ((index: Index, element: Element)) throws -> Void) rethrows {
        for element in zip(indices, self) { try body(element) }
    }
}

Test yapmak:

let list =  ["100","200","300","400","500"]
list.dropFirst(2).indexedElements {
    print("Index:", $0.index, "Element:", $0.element)
}

Bu p [rint

Dizin: 2 Eleman: 300

Dizin: 3 Eleman: 400

Dizin: 4 Eleman: 500


BTW Uygulayabileceğiniz enumeratedIndicesile üzerinde döngü ilezip(self.indices, self)
eski durumuna Monica - Alexander

@ Alexander-ReinstateMonica for element in zip(indices, self) { try body(element) }. Btw Ben seçtiğim adlandırma sevmiyorum, indexedElementsne yaptığını daha iyi açıklayabilir
Leo Dabus

Ooo bence bu daha iyi bir isim. Evet, bir fordöngü işe yarıyor, ama aynı zamandazip(self.indices, self) .forEach(body)
Alexander - Monica'yı yeniden ele

@ Alexander-ReinstateMonica forEachperde arkasında for döngüsü yapıyor. Basit tutmayı tercih ediyorum github.com/apple/swift/blob/master/stdlib/public/core/… @inlinable public func forEach( _ body: (Element) throws -> Void ) rethrows { for element in self { try body(element) } } }
Leo Dabus

7

Bu, Numaralandırma döngüsünün Formülüdür:

for (index, value) in shoppingList.enumerate() {
print("Item \(index + 1): \(value)")
}

daha fazla ayrıntı için Buradan kontrol edebilirsiniz .


5

Şahsen forEachyöntemi kullanmayı tercih ediyorum :

list.enumerated().forEach { (index, element) in
    ...
}

Kısa sürümü de kullanabilirsiniz:

list.enumerated().forEach { print("index: \($0.0), value: \($0.1)") }

4

Xcode 8 ve Swift 3: Dizi kullanılarak numaralandırılabilir tempArray.enumerated()

Misal:

var someStrs = [String]()

someStrs.append("Apple")  
someStrs.append("Amazon")  
someStrs += ["Google"]    


for (index, item) in someStrs.enumerated()  
{  
        print("Value at index = \(index) is \(item)").  
}

konsolu:

Value at index = 0 is Apple
Value at index = 1 is Amazon
Value at index = 2 is Google

3

Kullanmak isteyenler için forEach .

Hızlı 4

extension Array {
  func forEachWithIndex(_ body: (Int, Element) throws -> Void) rethrows {
    try zip((startIndex ..< endIndex), self).forEach(body)
  }
}

Veya

array.enumerated().forEach { ... }

2

Yapmanız isteyen ne için, kullanması gereken enumerated()senin üzerine yöntemini Array :

for (index, element) in list.enumerated() {
    print("\(index) - \(element)")
}

-1

Bunu uygulamak için numaralandırma fonksiyonunu çağırdık. sevmek

array.enumerate () içindeki (dizin, öğe) için {

// dizin, dizinin dizin konumlandırmasıdır

// öğesi dizinin öğesidir

}

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.