Как вернуть первые 5 объектов массива в Swift?

179

Есть ли в Swift умный способ использования методов более высокого порядка в массиве для возврата 5 первых объектов? Целью obj-c было сохранение индекса и цикл по индексу приращения массива, пока он не достиг 5, и возврат нового массива. Есть ли способ сделать это filter, mapили reduce?

Боген
источник
Смотрите мой ответ для Swift 4, который показывает до 6 различных способов вернуть первые nэлементы Array.
Imanou Petit

Ответы:

446

Безусловно, самый лучший способ получить первые N элементов массива Swift - это использовать prefix(_ maxLength: Int):

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

Это имеет преимущество в том, чтобы быть безопасным. Если счетчик, на который вы передаете, prefixбольше, чем счетчик массива, он просто возвращает весь массив.

ПРИМЕЧАНИЕ: как указано в комментариях, Array.prefixфактически возвращает ArraySlice, а не Array. В большинстве случаев это не должно иметь значения, но если вам нужно присвоить результат Arrayтипу или передать его методу, ожидающему Arrayпараметр, вам нужно будет принудительно преобразовать результат в Arrayтип:let first5 = Array(someArray.prefix(5))

mluisbrown
источник
42
+1 такой плохой выбор имени, ИМХО. Массив имеет dropFirstи dropLast, так же может иметь takeFirstи takeLast.
Мазёд
10
Также, если вам нужно first5быть массивом, просто напишитеlet first5 = Array(someArray.prefix(5))
ULazdins
2
@mluisbrown Извините, не могу с вами согласиться :) В моем проекте video = video.prefix(5)в Xcode 7.2 возникает ошибка компиляции Cannot assign value of type 'ArraySlice<Video>' to type '[Video]'
ULazdins
5
video = Array(video.prefix(5))
Bartłomiej Semańczyk
2
@ onmyway133 prefixвозможно только в Swift 2.x (я не помню, был ли он в 1.x), но он, безусловно, существует в любой ОС, которая поддерживает Swift 2.x, то есть iOS 7 и выше. Доступность функции Swift определяется версиями Swift, а не версиями для iOS.
mluisbrown
99

Обновление: теперь есть возможность использовать prefixдля получения первых n элементов массива. Проверьте ответ @ mluisbrown для объяснения того, как использовать префикс.

Оригинал Ответ: Вы можете сделать это очень легко без filter, mapили reduce, просто возвращает диапазон вашего массива:

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

var firstFive = wholeArray[0..<n] // 1,2,3,4,5
Кристиан
источник
71
Если вам нужны только первые nэлементы из массива Swift, вы можете сделать wholeArray.prefix(n)это с дополнительным преимуществом безопасности границ. Если nбольше, чем размер массива, prefixвозвращает весь массив.
mluisbrown
4
Он потерпит крах, если wholeArrayне будет иметь больше элементов, чемn
Джейк Лин
@Crashalot Я не совсем уверен, но я думаю, что в то время, когда я написал свой ответ, не было такой вещи, как prefix.
Кристиан
1
Используйте этот код, чтобы получить первые 5 объектов ... Это прекрасно работает [0,1,2,3,4,5] .enumerated (). FlatMap {$ 0 <5? $ 1: ноль}
Маниш Махаджан
64

С Swift 5, в соответствии с вашими потребностями, вы можете выбрать один из 6 следующих кодов игровой площадки , чтобы решить вашу проблему.


# 1. Используя subscript(_:)индекс

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. Используя prefix(_:)метод

Сложность: O (1), если коллекция соответствует RandomAccessCollection; в противном случае O ( k ), где k - количество элементов, которые нужно выбрать в начале коллекции.

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 заявляет для prefix(_:):

Если максимальная длина превышает количество элементов в коллекции, результат содержит все элементы в коллекции.


# 3. Используя prefix(upTo:)метод

Сложность: 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 заявляет для prefix(upTo:):

Использование prefix(upTo:)метода эквивалентно использованию частично полуоткрытого диапазона в качестве индекса коллекции. Нижний индекс предпочтительнее, чем prefix(upTo:).


# 4. Используя prefix(through:)метод

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. Используя removeSubrange(_:)метод

Сложность: O ( n ), где n - длина коллекции.

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. Используя dropLast(_:)метод

Сложность: O (1), если коллекция соответствует RandomAccessCollection; в противном случае O ( k ), где k - количество удаляемых элементов.

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
источник
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
источник
18

SWIFT 4

Другое решение:

Простое встроенное решение, которое не вылетит, если ваш массив слишком короткий

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

Но отлично работает с этим.

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

Обычно это вылетает, если вы сделали это:

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

[0,1,2,3,4,5].prefix(1000) // THIS DOESNT
Дэвид Рис
источник
3
для swift3[0,1,2,3,4,5].enumerated().flatMap{ $0.offset < 1000 ? $0.element : nil }
StevenTsooo
1
Спасибо! Если вы хотите обновить, flatMap теперь называется compactMap в Swift 4 для сжатия массива.
мужчина
14

Чтобы получить первые 5 элементов массива, все, что вам нужно сделать, это нарезать нужный массив. В Swift, вы делаете это так: array[0..<5].

Чтобы сделать выбор первых N элементов массива более функциональным и обобщенным, вы можете создать метод расширения для этого. Например:

extension Array {
    func takeElements(var elementCount: Int) -> Array {
        if (elementCount > count) {
            elementCount = count
        }
        return Array(self[0..<elementCount])
    }
}
Markus
источник
5

Я немного изменил ответ Маркуса, чтобы обновить его до последней версии Swift, так как varвнутри вашего объявления метода больше не поддерживается:

extension Array {
    func takeElements(elementCount: Int) -> Array {
        if (elementCount > count) {
            return Array(self[0..<count])
        }
        return Array(self[0..<elementCount])
    }
}
Antoine
источник
3

Swift 4

Чтобы получить первые N элементов массива Swift, вы можете использовать prefix(_ maxLength: Int):

Array(largeArray.prefix(5))
quemeful
источник
1

Swift 4 с сохранением типов массивов

extension Array {
    func take(_ elementsCount: Int) -> [Element] {
        let min = Swift.min(elementsCount, count)
        return Array(self[0..<min])
    }
}
Павел Шорохов
источник
1

Обновление для Swift 4:

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

Для быстрой 3:

[0,1,2,3,4,5].enumerated().flatMap{ $0 < 10000 ? $1 : nil }
mkkrolik
источник
1
Неэффективное решение. 1) Вы создаете дополнительную последовательность длины массива. 2) Вы перебираете все элементы.
Александр Бекерт
2
10000? Что это такое?
BangOperator
1

Простой и Простой

extension Array {
    func first(elementCount: Int) -> Array {
          let min = Swift.min(elementCount, count)
          return Array(self[0..<min])
    }
}
Абхишек Беди
источник
1

Для массива объектов вы можете создать расширение из Sequence.

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

Использование:

struct Apple {}

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

// limitTwoApples: [Apple(), Apple()]
Лев Валентим
источник