Jak zwrócić pierwsze 5 obiektów Array w Swift?

179

Czy w języku Swift istnieje sprytny sposób na użycie metod wyższego rzędu w Array do zwrócenia 5 pierwszych obiektów? Sposób według obj-c polegał na zapisaniu indeksu i wykonaniu pętli for przez tablicę, zwiększając indeks do wartości 5 i zwracając nową tablicę. Czy istnieje sposób to zrobić z filter, mapalbo reduce?

bogen
źródło
Zobacz moją odpowiedź dla Swift 4, która pokazuje do 6 różnych sposobów zwrócenia pierwszych nelementów pliku Array.
Imanou Petit

Odpowiedzi:

445

Zdecydowanie najładniejszym sposobem uzyskania pierwszych N elementów tablicy Swift jest użycie prefix(_ maxLength: Int):

let someArray = [1, 2, 3, 4, 5, 6, 7]
let first5 = someArray.prefix(5) // 1, 2, 3, 4, 5

Ma to tę zaletę, że jest bezpieczny. Jeśli liczba, do której przekazujesz, prefixjest większa niż liczba tablic, po prostu zwraca całą tablicę.

UWAGA: jak wskazano w komentarzach, w Array.prefixrzeczywistości zwraca an ArraySlice, a nie Array. W większości przypadków nie powinno to robić różnicy, ale jeśli chcesz przypisać wynik do Arraytypu lub przekazać go do metody, która oczekuje Arrayparametru, będziesz musiał wymusić wynik na Arraytyp:let first5 = Array(someArray.prefix(5))

mluisbrown
źródło
42
+1 taki zły wybór nazwy, IMHO. Array ma dropFirsti dropLast, więc równie dobrze mógłby mieć takeFirsti takeLast.
Mozyod
10
Również jeśli chcesz first5być tablicą, po prostu napiszlet first5 = Array(someArray.prefix(5))
ULazdins
2
@mluisbrown Przepraszamy, nie mogę się z Tobą zgodzić :) W moim projekcie video = video.prefix(5)w Xcode 7.2 powoduje błąd kompilacji Cannot assign value of type 'ArraySlice<Video>' to type '[Video]'
ULazdins
5
video = Array(video.prefix(5))
Bartłomiej Semańczyk
2
@ onmyway133 prefixjest prawdopodobnie tylko Swift 2.x (nie pamiętam, czy był w 1.x), ale z pewnością istnieje w każdym systemie operacyjnym obsługującym Swift 2.x, czyli iOS 7 i nowsze. Dostępność funkcji Swift jest określana przez wersje Swift, a nie wersje iOS.
mluisbrown
99

Aktualizacja: istnieje teraz możliwość użycia prefixdo pobrania pierwszych n elementów tablicy. Sprawdź odpowiedź @ mluisbrown, aby uzyskać wyjaśnienie, jak używać prefiksu.

Oryginalny Odpowiedź: Można to zrobić bardzo łatwo, bez filter, maplub reducepo prostu powrocie zakres swojej tablicy:

var wholeArray = [1, 2, 3, 4, 5, 6]
var n = 5

var firstFive = wholeArray[0..<n] // 1,2,3,4,5
chrześcijanin
źródło
71
Jeśli chcesz tylko pierwszych nelementów z tablicy Swift, możesz to zrobić, wholeArray.prefix(n)co ma dodatkową zaletę w postaci bezpieczeństwa granic. Jeśli njest większy niż rozmiar tablicy, prefixzwraca całą tablicę.
mluisbrown
4
Będzie się zawieszał, jeśli wholeArraynie ma więcej elementów niżn
Jake Lin
@Crashalot Nie jestem do końca pewien, ale myślę, że w momencie, w którym pisałem swoją odpowiedź, nie było czegoś takiego jak prefix.
Christian,
1
Użyj tego kodu, aby uzyskać pierwsze 5 obiektów ... Działa to doskonale [0,1,2,3,4,5] .enumerated (). FlatMap {$ 0 <5? $ 1: nil}
Manish Mahajan
64

Dzięki Swift 5, w zależności od potrzeb, możesz wybrać jeden z 6 poniższych kodów Playground , aby rozwiązać problem.


# 1. Korzystanie z subscript(_:)indeksu dolnego

let array = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L"]
let arraySlice = array[..<5]
//let arraySlice = array[0..<5] // also works
//let arraySlice = array[0...4] // also works
//let arraySlice = array[...4] // also works
let newArray = Array(arraySlice)
print(newArray) // prints: ["A", "B", "C", "D", "E"]

# 2. Korzystanie z prefix(_:)metody

Złożoność: O (1) jeśli kolekcja jest zgodna RandomAccessCollection; w przeciwnym razie O ( k ), gdzie k to liczba elementów do wybrania od początku kolekcji.

let array = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L"]
let arraySlice = array.prefix(5)
let newArray = Array(arraySlice)
print(newArray) // prints: ["A", "B", "C", "D", "E"]

Apple stwierdza prefix(_:):

Jeśli maksymalna długość przekracza liczbę elementów w kolekcji, wynik zawiera wszystkie elementy w kolekcji.


# 3. Korzystanie z prefix(upTo:)metody

Złożoność: O (1)

let array = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L"]
let arraySlice = array.prefix(upTo: 5)
let newArray = Array(arraySlice)
print(newArray) // prints: ["A", "B", "C", "D", "E"]

Apple stwierdza prefix(upTo:):

Użycie tej prefix(upTo:)metody jest równoważne z użyciem częściowego półotwartego zakresu jako indeksu kolekcji. Notacja z indeksem dolnym jest preferowana prefix(upTo:).


# 4. Korzystanie z prefix(through:)metody

let array = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L"]
let arraySlice = array.prefix(through: 4)
let newArray = Array(arraySlice)
print(newArray) // prints: ["A", "B", "C", "D", "E"]

# 5. Korzystanie z removeSubrange(_:)metody

Złożoność: O ( n ), gdzie n jest długością kolekcji.

var array = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L"]
array.removeSubrange(5...)
print(array) // prints: ["A", "B", "C", "D", "E"]

# 6. Korzystanie z dropLast(_:)metody

Złożoność: O (1) jeśli kolekcja jest zgodna RandomAccessCollection; w przeciwnym razie O ( k ), gdzie k to liczba elementów do usunięcia.

let array = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L"]
let distance = array.distance(from: 5, to: array.endIndex)
let arraySlice = array.dropLast(distance)
let newArray = Array(arraySlice)
print(newArray) // prints: ["A", "B", "C", "D", "E"]
Imanou Petit
źródło
28
let a: [Int] = [0, 0, 1, 1, 2, 2, 3, 3, 4]
let b: [Int] = Array(a.prefix(5))
// result is [0, 0, 1, 1, 2]
neoneye
źródło
18

SWIFT 4

Inne rozwiązanie:

Proste rozwiązanie wbudowane, które nie ulegnie awarii, jeśli twoja tablica jest zbyt krótka

[0,1,2,3,4,5].enumerated().compactMap{ $0.offset < 3 ? $0.element : nil }

Ale działa dobrze z tym.

[0,1,2,3,4,5].enumerated().compactMap{ $0.offset < 1000 ? $0.element : nil }

Zwykle to się zawiesiło, gdybyś zrobił to:

[0,1,2,3,4,5].prefix(upTo: 1000) // THIS CRASHES

[0,1,2,3,4,5].prefix(1000) // THIS DOESNT
David Rees
źródło
3
dla swift3[0,1,2,3,4,5].enumerated().flatMap{ $0.offset < 1000 ? $0.element : nil }
StevenTsooo
1
Dzięki! W przypadku, gdy chcesz zaktualizować, flatMap nazywa się teraz compactMap w Swift 4 do kompaktowania tablic.
manmal
14

Aby uzyskać pierwsze 5 elementów tablicy, wystarczy wyciąć tę tablicę. W Swift, robisz to tak: array[0..<5].

Aby sprawić, że wybieranie N pierwszych elementów tablicy będzie nieco bardziej funkcjonalne i możliwe do uogólnienia, możesz utworzyć metodę rozszerzającą, aby to zrobić. Na przykład:

extension Array {
    func takeElements(var elementCount: Int) -> Array {
        if (elementCount > count) {
            elementCount = count
        }
        return Array(self[0..<elementCount])
    }
}
Markus
źródło
5

Nieznacznie zmieniłem odpowiedź Markusa, aby zaktualizować ją do najnowszej wersji Swift, ponieważ varwewnątrz deklaracji metody nie jest już obsługiwana:

extension Array {
    func takeElements(elementCount: Int) -> Array {
        if (elementCount > count) {
            return Array(self[0..<count])
        }
        return Array(self[0..<elementCount])
    }
}
Antoine
źródło
3

Szybki 4

Aby uzyskać pierwsze N ​​elementów tablicy Swift, możesz użyć prefix(_ maxLength: Int):

Array(largeArray.prefix(5))
pytający
źródło
1

Swift 4 z zapisywaniem typów tablic

extension Array {
    func take(_ elementsCount: Int) -> [Element] {
        let min = Swift.min(elementsCount, count)
        return Array(self[0..<min])
    }
}
Pavel Shorokhov
źródło
1

Aktualizacja dla Swift 4:

[0,1,2,3,4,5].enumerated().compactMap{ $0 < 10000 ? $1 : nil }

Dla Swift 3:

[0,1,2,3,4,5].enumerated().flatMap{ $0 < 10000 ? $1 : nil }
mkkrolik
źródło
1
Nieskuteczne rozwiązanie. 1) Tworzysz dodatkową sekwencję długości tablicy. 2) Powtarzasz wszystkie elementy.
Alexander Bekert,
2
dziesięć tysięcy? Co to jest?
BangOperator
1

Jasne i proste

extension Array {
    func first(elementCount: Int) -> Array {
          let min = Swift.min(elementCount, count)
          return Array(self[0..<min])
    }
}
Abhishek Bedi
źródło
1

Dla tablicy obiektów możesz utworzyć rozszerzenie z Sequence.

extension Sequence {
    func limit(_ max: Int) -> [Element] {
        return self.enumerated()
            .filter { $0.offset < max }
            .map { $0.element }
    }
}

Stosowanie:

struct Apple {}

let apples: [Apple] = [Apple(), Apple(), Apple()]
let limitTwoApples = apples.limit(2)

// limitTwoApples: [Apple(), Apple()]
Leo Valentim
źródło