Czy istnieje funkcja, której można użyć do iteracji po tablicy i posiadania zarówno indeksu, jak i elementu, takich jak wyliczenie Pythona?
for index, element in enumerate(list):
...
Czy istnieje funkcja, której można użyć do iteracji po tablicy i posiadania zarówno indeksu, jak i elementu, takich jak wyliczenie Pythona?
for index, element in enumerate(list):
...
Odpowiedzi:
Tak. Począwszy od wersji Swift 3.0, jeśli potrzebujesz indeksu dla każdego elementu wraz z jego wartością, możesz użyć enumerated()
metody do iteracji po tablicy. Zwraca ciąg par złożony z indeksu i wartości dla każdego elementu w tablicy. Na przykład:
for (index, element) in list.enumerated() {
print("Item \(index): \(element)")
}
Przed Swift 3.0 i po Swift 2.0 funkcja ta została wywołana enumerate()
:
for (index, element) in list.enumerate() {
print("Item \(index): \(element)")
}
Przed wersją Swift 2.0 enumerate
była funkcją globalną.
for (index, element) in enumerate(list) {
println("Item \(index): \(element)")
}
enumerate
?
enumerating
na Swift 4. Ekscytujące!
for (index, element) in
podczas używania enumerated
wprowadza w błąd. powinien byćfor (offset, element) in
Swift 5 zapewnia metodę o nazwie enumerated()
dla Array
. enumerated()
ma następującą deklarację:
func enumerated() -> EnumeratedSequence<Array<Element>>
Zwraca sekwencję par (n, x), gdzie n oznacza kolejną liczbę całkowitą rozpoczynającą się od zera, a x oznacza element sekwencji.
W najprostszych przypadkach możesz użyć enumerated()
pętli for. Na przykład:
let list = ["Car", "Bike", "Plane", "Boat"]
for (index, element) in list.enumerated() {
print(index, ":", element)
}
/*
prints:
0 : Car
1 : Bike
2 : Plane
3 : Boat
*/
Zauważ jednak, że nie jesteś ograniczony do używania enumerated()
z pętlą for. W rzeczywistości, jeśli planujesz używać enumerated()
pętli for dla czegoś podobnego do następującego kodu, robisz to źle:
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)]
Szybszym sposobem na to jest:
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)]
Alternatywnie możesz również używać enumerated()
z 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]]
Co więcej, chociaż ma pewne ograniczenia , forEach
może być dobrym zamiennikiem pętli for:
let list = [Int](1...5)
list.reversed().enumerated().forEach { print($0, ":", $1) }
/*
prints:
0 : 5
1 : 4
2 : 3
3 : 2
4 : 1
*/
Korzystając z enumerated()
i makeIterator()
, możesz nawet iterować ręcznie na swoim Array
. Na przykład:
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
*/
enumerate
?
Znalazłem tę odpowiedź, szukając sposobu, aby to zrobić za pomocą Słownika , i okazuje się, że dość łatwo ją dostosować, wystarczy podać krotkę dla elementu.
// Swift 2
var list = ["a": 1, "b": 2]
for (index, (letter, value)) in list.enumerate() {
print("Item \(index): \(letter) \(value)")
}
Możesz po prostu użyć pętli wyliczenia, aby uzyskać pożądany wynik:
Swift 2:
for (index, element) in elements.enumerate() {
print("\(index): \(element)")
}
Swift 3 i 4:
for (index, element) in elements.enumerated() {
print("\(index): \(element)")
}
Możesz też po prostu przejść przez pętlę for, aby uzyskać ten sam wynik:
for index in 0..<elements.count {
let element = elements[index]
print("\(index): \(element)")
}
Mam nadzieję, że to pomoże.
for (index, element) in arrayOfValues.enumerate() {
// do something useful
}
lub z Swift 3 ...
for (index, element) in arrayOfValues.enumerated() {
// do something useful
}
Jednak najczęściej używam wyliczenia w połączeniu z mapą lub filtrem. Na przykład z operowaniem na kilku tablicach.
W tej tablicy chciałem filtrować nieparzyste lub nawet indeksowane elementy i konwertować je z Ints na Doubles. W ten sposób enumerate()
dostajesz indeks i element, a następnie filtr sprawdza indeks, a na koniec, aby pozbyć się wynikowej krotki, mapuję go tylko na element.
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)
})
Korzystanie z .enumerate()
działa, ale nie zapewnia prawdziwego indeksu elementu; zapewnia tylko wartość Int zaczynającą się od 0 i zwiększającą się o 1 dla każdego kolejnego elementu. Jest to zwykle nieistotne, ale istnieje ryzyko nieoczekiwanego zachowania w przypadku użycia z tym ArraySlice
typem. Weź następujący kod:
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
Jest to nieco wymyślony przykład i nie jest to częsty problem w praktyce, ale nadal uważam, że warto wiedzieć, że tak się może stać.
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 element
Tak, dlatego nazywa się to wyliczeniem . Ponadto plasterek nie jest tablicą, więc nic dziwnego, że zachowuje się inaczej. Tutaj nie ma błędu - wszystko jest zgodne z projektem. :)
filter
pierwszego?
Aby uzyskać kompletność, możesz po prostu iterować indeksy tablicowe i użyć indeksu dolnego, aby uzyskać dostęp do elementu o odpowiednim indeksie:
let list = [100,200,300,400,500]
for index in list.indices {
print("Element at:", index, " Value:", list[index])
}
Korzystanie z forEach
list.indices.forEach {
print("Element at:", $0, " Value:", list[$0])
}
Przy użyciu enumerated()
metody zbierania . Zauważ, że zwraca kolekcję krotek wraz offset
z element
:
for item in list.enumerated() {
print("Element at:", item.offset, " Value:", item.element)
}
za pomocą forEach:
list.enumerated().forEach {
print("Element at:", $0.offset, " Value:", $0.element)
}
Te zostaną wydrukowane
Element w: 0 Wartość: 100
Element w: 1 Wartość: 200
Element w: 2 Wartość: 300
Element na: 3 Wartość: 400
Element w: 4 Wartość: 500
Jeśli potrzebujesz indeksu tablicy (a nie przesunięcia) i jego elementu, możesz rozszerzyć kolekcję i stworzyć własną metodę uzyskiwania indeksowanych elementów:
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)
}
}
}
Inną możliwą implementacją sugerowaną przez Alexa jest skompresowanie indeksów kolekcji za pomocą jej elementów:
extension Collection {
func indexedElements(body: ((index: Index, element: Element)) throws -> Void) rethrows {
for element in zip(indices, self) { try body(element) }
}
}
Testowanie:
let list = ["100","200","300","400","500"]
list.dropFirst(2).indexedElements {
print("Index:", $0.index, "Element:", $0.element)
}
Spowoduje to [rint
Indeks: 2 Element: 300
Indeks: 3 element: 400
Indeks: 4 Element: 500
enumeratedIndices
, zapętlając zzip(self.indices, self)
for element in zip(indices, self) { try body(element) }
. Przy okazji nie podoba mi się nazwa, którą wybrałem, indexedElements
może lepiej opisać, co robi
for
pętla działa, ale takżezip(self.indices, self) .forEach(body)
forEach
robi pętlę for za kulisami. Wolę zachować prostotę 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) } } }
Xcode 8 i Swift 3: Array można wyliczyć za pomocą tempArray.enumerated()
Przykład:
var someStrs = [String]()
someStrs.append("Apple")
someStrs.append("Amazon")
someStrs += ["Google"]
for (index, item) in someStrs.enumerated()
{
print("Value at index = \(index) is \(item)").
}
konsola:
Value at index = 0 is Apple
Value at index = 1 is Amazon
Value at index = 2 is Google
Dla tych, którzy chcą korzystać forEach
.
Szybki 4
extension Array {
func forEachWithIndex(_ body: (Int, Element) throws -> Void) rethrows {
try zip((startIndex ..< endIndex), self).forEach(body)
}
}
Lub
array.enumerated().forEach { ... }
Do tego, co chcesz zrobić, powinieneś użyć enumerated()
metody na tablicy :
for (index, element) in list.enumerated() {
print("\(index) - \(element)")
}
W celu zaimplementowania tej funkcji wywołaliśmy funkcję wyliczającą. lubić
for (indeks, element) w array.enumerate () {
// index jest pozycją indeksu tablicy
// element jest elementem tablicy
}