Як перевірити, чи є елемент у масиві


475

Як у Swift я можу перевірити, чи існує елемент у масиві? Xcode не пропонує жодних пропозицій щодо contain, includeабо has, і швидкий пошук книги не призвів до нічого. Будь-яка ідея, як це перевірити? Я знаю, що існує метод, findякий повертає число індексу, але чи є метод, який повертає булевий, як рубін #include??

Приклад того, що мені потрібно:

var elements = [1,2,3,4,5]
if elements.contains(5) {
  //do something
}

11
if find(elements, 5) != nil { }недостатньо добре?
Мартін Р

1
Я сподівався на щось ще чистіше, але це не виглядає добре. У документації чи книзі я ще нічого не знайшов.
Джаредсміт

Відповіді:


860

Швидкий 2, 3, 4, 5:

let elements = [1, 2, 3, 4, 5]
if elements.contains(5) {
    print("yes")
}

contains()є методом розширення протоколу з SequenceType(для послідовностей Equatableелементів) , а не глобальний метод , як і в більш ранніх версіях.

Зауваження:

Швидкі старі версії:

let elements = [1,2,3,4,5]
if contains(elements, 5) {
    println("yes")
}

4
Будь-яка документація про такі глобальні функції?
Рівера

3
Чи має це працювати, якщо кожен елемент у масиві (і елемент, який ми шукаємо) має тип словника <String, AnyObject>? Намагаючись досягти цього, але я отримую помилку під час компіляції.
ppalancica

7
@ppalancica: Це вимагає, щоб елементи масиву відповідали Equatableпротоколу (що Dictionary<String, AnyObject>не відповідає). Є другий варіант, contains()який бере присудок (порівняйте stackoverflow.com/questions/29679486/… ), можливо, ви можете використовувати це, наприкладif contains(array, { $0 == dict } ) ...
Martin R

Як шукати конкретний елемент із загального масиву? сказати [AnyObject]?
Дхаваль Х. Нена

127

Для тих, хто прийшов сюди, шукаючи знахідку та видаляючи об’єкт із масиву:

Швидкий 1

if let index = find(itemList, item) {
    itemList.removeAtIndex(index)
}

Швидкий 2

if let index = itemList.indexOf(item) {
    itemList.removeAtIndex(index)
}

Швидкий 3, 4

if let index = itemList.index(of: item) {
    itemList.remove(at: index)
}

Швидкий 5.2

if let index = itemList.firstIndex(of: item) {
    itemList.remove(at: index)
}

3
Будь ласка, дайте відповідь на питання. Це питання просто задає питання про пошук елемента в масиві, а не для видалення чи оновлення. Ви можете поставити окреме запитання і відповісти на нього також самі.
Tejas

60

Скористайтеся цим розширенням:

extension Array {
    func contains<T where T : Equatable>(obj: T) -> Bool {
        return self.filter({$0 as? T == obj}).count > 0
    }
}

Використовувати як:

array.contains(1)

Оновлено для Swift 2/3

Зауважте, що, як і для Swift 3 (або навіть 2), розширення більше не потрібне, оскільки глобальна containsфункція перетворена на пару методу розширення Array, що дозволяє вам виконувати будь-яке з:

let a = [ 1, 2, 3, 4 ]

a.contains(2)           // => true, only usable if Element : Equatable

a.contains { $0 < 1 }   // => false

10
пошук швидше.
Джим Балтер

1
залежно від того, до чого ти звик, .contain може відчувати себе більш інтуїтивно і незабутнім
Pirijan

4
Чи можете ви пояснити свій синтаксис, розбивши його? Я ніколи не бачив такого форматування раніше, і у вас відразу багато вдосконалених матеріалів!
Агресор

40

Якщо ви перевіряєте, чи міститься в масиві примірник спеціального класу або структури, перед використанням .contains (myObject) вам потрібно буде впровадити протокол Equatable .

Наприклад:

struct Cup: Equatable {
    let filled:Bool
}

static func ==(lhs:Cup, rhs:Cup) -> Bool { // Implement Equatable
    return lhs.filled == rhs.filled
}

тоді ви можете зробити:

cupArray.contains(myCup)

Порада : Переосмислення == має бути на глобальному рівні, а не у вашому класі / структурі


32

Я використовував фільтр.

let results = elements.filter { el in el == 5 }
if results.count > 0 {
    // any matching items are in results
} else {
    // not found
}

Якщо хочете, ви можете стиснути це

if elements.filter({ el in el == 5 }).count > 0 {
}

Сподіваюся, що це допомагає.


Оновлення для Swift 2

Ура за реалізацією за замовчуванням!

if elements.contains(5) {
    // any matching items are in results
} else {
    // not found
}

Мені подобається фільтр, тому що ви можете використовувати його для всіляких речей. Наприклад, я передавав якийсь код, який циклічно та петельно намагався перевірити, чи є в списку вже елемент із одним із його полів, що містить значення рядка. Це один рядок у Swift, використовуючи фільтр у цьому полі.
Maury Markowitz

Фільтр неефективний, тому що він завжди перетворюється на всі елементи, а не повертається негайно, коли елемент знайдений. Краще використовувати натомість пошук ().
Торстен

19

(Швидкий 3)

Перевірте, чи існує елемент у масиві (який відповідає деяким критеріям), і якщо так, продовжте роботу з першим таким елементом

Якщо ціль:

  1. Щоб перевірити, чи існує елемент у масиві (/ відповідає деяким булевим критеріям, не обов'язково тестуванням рівності),
  2. І якщо так, приступайте до роботи з першим таким елементом,

Тоді альтернатива , contains(_:)як blueprinted Sequenceє first(where:)з Sequence:

let elements = [1, 2, 3, 4, 5]

if let firstSuchElement = elements.first(where: { $0 == 4 }) {
    print(firstSuchElement) // 4
    // ...
}

У цьому надуманому прикладі його використання може здатися нерозумним, але це дуже корисно, якщо запитувати масиви не фундаментальних типів елементів для існування будь-яких елементів, які виконують певну умову. Напр

struct Person {
    let age: Int
    let name: String
    init(_ age: Int, _ name: String) {
        self.age = age
        self.name = name
    }
}

let persons = [Person(17, "Fred"),   Person(16, "Susan"),
               Person(19, "Hannah"), Person(18, "Sarah"),
               Person(23, "Sam"),    Person(18, "Jane")]

if let eligableDriver = persons.first(where: { $0.age >= 18 }) {
    print("\(eligableDriver.name) can possibly drive the rental car in Sweden.")
    // ...
} // Hannah can possibly drive the rental car in Sweden.

let daniel = Person(18, "Daniel")
if let sameAgeAsDaniel = persons.first(where: { $0.age == daniel.age }) {
    print("\(sameAgeAsDaniel.name) is the same age as \(daniel.name).")
    // ...
} // Sarah is the same age as Daniel.

Будь-які ланцюгові операції, що використовують, .filter { ... some condition }.firstможна вигідно замінити first(where:). Останні демонструють кращі наміри та мають переваги у порівнянні з можливими лінивими приладами .filter, оскільки вони пройдуть повний масив до вилучення (можливого) першого елемента, який проходить фільтр.


Перевірте, чи існує елемент у масиві (відповідає деяким критеріям), і якщо так, видаліть перший такий елемент

Коментар нижче запитів:

Як я можу видалити firstSuchElementз масиву?

Аналогічний випадок використання, як описано вище, полягає у видаленні першого елемента, який виконує даний присудок. Для цього index(where:)метод Collection(який легко доступний для збирання масиву) може бути використаний для пошуку індексу першого елемента, що виконує присудок, після чого індекс можна використовувати remove(at:)методом Arrayдо (можливо; враховуючи, що він існує) видаліть цей елемент.

var elements = ["a", "b", "c", "d", "e", "a", "b", "c"]

if let indexOfFirstSuchElement = elements.index(where: { $0 == "c" }) {
    elements.remove(at: indexOfFirstSuchElement)
    print(elements) // ["a", "b", "d", "e", "a", "b", "c"]
}

Або, якщо ви хочете вилучити елемент з масиву та працювати з ним , застосуйте Optional: s map(_:)метод для умовного (для .some(...)повернення з index(where:)) використання результату index(where:)для видалення та захоплення вилученого елемента з масиву (в рамках необов'язкового прив'язуючого пункту) .

var elements = ["a", "b", "c", "d", "e", "a", "b", "c"]

if let firstSuchElement = elements.index(where: { $0 == "c" })
    .map({ elements.remove(at: $0) }) {

    // if we enter here, the first such element have now been
    // remove from the array
    print(elements) // ["a", "b", "d", "e", "a", "b", "c"]

    // and we may work with it
    print(firstSuchElement) // c
}

Зауважте, що у надуманому прикладі над членами масиву є прості типи значень ( Stringекземпляри), тому використання предиката для пошуку заданого члена є дещо надмірним вбивством, оскільки ми можемо просто перевірити рівність за допомогою більш простого index(of:)методу, як показано у відповіді @ DogCoffee . Якщо застосувати підхід пошуку та видалення вище до Personприкладу, однак, використання index(where:)з присудком є ​​доцільним (оскільки ми більше не перевіряємо рівність, а виконуємо доданий присудок).


Як я можу видалити firstSuchElement з масиву?
i6x86

@ i6x86 дякую за запитання. Я оновив свою відповідь прикладом того, як видалити елемент (а також як видалити та захопити видалений елемент).
dfri

14

Найпростіший спосіб досягти цього - використовувати фільтр на масиві.

let result = elements.filter { $0==5 }

resultматиме знайдений елемент, якщо він існує, і буде порожнім, якщо елемент не існує. Тож просто перевіряючи, чи resultпорожній він, вам скаже, чи існує елемент у масиві. Я б використав наступне:

if result.isEmpty {
    // element does not exist in array
} else {
    // element exists
}

чудове рішення. тому цей метод повертає масив. Однак я використовую це для пошуку "ідентифікатора". У моїй заявці d унікальні, тому результат може бути лише один. Чи є спосіб повернути лише 1 результат? Я зараз використовую результат [0]
Dan Beaulieu

3
@DanBeaulieu Виконуючи щось подібне, let result = elements.filter { $0==5 }.firstслід досягти того, що ви шукаєте.
davetw12

7

Швидкий 4/5

Ще один спосіб досягти цього - за допомогою функції фільтра

var elements = [1,2,3,4,5]
if let object = elements.filter({ $0 == 5 }).first {
    print("found")
} else {
    print("not found")
}

6

Станом на Swift 2.1 NSArrays containsObjectможна використовувати так:

if myArray.containsObject(objectImCheckingFor){
    //myArray has the objectImCheckingFor
}

4
Насправді це для NSArray. Не швидкий масив
Tycho Pandelaar

Так, але ви можете тимчасово перетворити свій швидкий масив у NSArray: якщо дозволити tempNSArrayForChecking = mySwiftArray як NSArray? де tempNSArrayForChecking.containsObject (objectImCheckingFor) {// myArray має об’єкт}
Vitalii

4

На всякий випадок, коли хтось намагається знайти, чи indexPathє серед обраних (наприклад, в a UICollectionViewабо UITableView cellForItemAtIndexPathфункціях):

    var isSelectedItem = false
    if let selectedIndexPaths = collectionView.indexPathsForSelectedItems() as? [NSIndexPath]{
        if contains(selectedIndexPaths, indexPath) {
            isSelectedItem = true
        }
    }

4

Масив

let elements = [1, 2, 3, 4, 5, 5]

Перевірте наявність елементів

elements.contains(5) // true

Отримайте індекс елементів

elements.firstIndex(of: 5) // 4
elements.firstIndex(of: 10) // nil

Отримати кількість елементів

let results = elements.filter { element in element == 5 }
results.count // 2

3

Ось моє невелике розширення, яке я щойно написав, щоб перевірити, чи містить у своєму масиві делегат об’єкт делегата чи ні ( Swift 2 ). :) Він також працює з типовими значеннями, як шарм.

extension Array
{
    func containsObject(object: Any) -> Bool
    {
        if let anObject: AnyObject = object as? AnyObject
        {
            for obj in self
            {
                if let anObj: AnyObject = obj as? AnyObject
                {
                    if anObj === anObject { return true }
                }
            }
        }
        return false
    }
}

Якщо у вас є ідея, як оптимізувати цей код, ніж просто дайте мені знати.


2

якщо користувач знайде конкретні елементи масиву, використовуйте нижче код, такий же як ціле значення.

var arrelemnts = ["sachin", "test", "test1", "test3"]

 if arrelemnts.contains("test"){
    print("found")   }else{
    print("not found")   }

2

Швидкий

Якщо ви не використовуєте об'єкт, ви можете користуватися цим кодом.

let elements = [ 10, 20, 30, 40, 50]

if elements.contains(50) {

   print("true")

}

Якщо ви швидко використовуєте клас NSObject. Ця змінна відповідає моїй вимозі. ви можете змінити для своєї вимоги.

var cliectScreenList = [ATModelLeadInfo]()
var cliectScreenSelectedObject: ATModelLeadInfo!

Це для одного типу даних.

{ $0.user_id == cliectScreenSelectedObject.user_id }

Якщо ви хочете ввести AnyObject.

{ "\($0.user_id)" == "\(cliectScreenSelectedObject.user_id)" }

Повна умова

if cliectScreenSelected.contains( { $0.user_id == cliectScreenSelectedObject.user_id } ) == false {

    cliectScreenSelected.append(cliectScreenSelectedObject)

    print("Object Added")

} else {

    print("Object already exists")

 }

1

як щодо використання хеш-таблиці для роботи, як це?

по-перше, створення загальної функції "хеш-карти", розширення протоколу послідовності.

extension Sequence where Element: Hashable {

    func hashMap() -> [Element: Int] {
        var dict: [Element: Int] = [:]
        for (i, value) in self.enumerated() {
            dict[value] = i
        }
        return dict
    }
}

Це розширення буде працювати до тих пір, поки елементи в масиві відповідають Хашибелу, як цілі чи рядки, ось використання ...

let numbers = Array(0...50) 
let hashMappedNumbers = numbers.hashMap()

let numToDetect = 35

let indexOfnumToDetect = hashMappedNumbers[numToDetect] // returns the index of the item and if all the elements in the array are different, it will work to get the index of the object!

print(indexOfnumToDetect) // prints 35

Але поки що, давайте просто зосередимось, щоб перевірити, чи є елемент у масиві.

let numExists = indexOfnumToDetect != nil // if the key does not exist 
means the number is not contained in the collection.

print(numExists) // prints true

0

Swift 4.2 +
Ви можете легко перевірити, чи є ваш екземпляр масивом чи ні, виконавши наступну функцію.

func verifyIsObjectOfAnArray<T>(_ object: T) -> Bool {
   if let _ = object as? [T] {
      return true
   }

   return false
}

Навіть ви можете отримати доступ до нього наступним чином. Ви отримаєте, nilякщо об’єкт не буде масивом.

func verifyIsObjectOfAnArray<T>(_ object: T) -> [T]? {
   if let array = object as? [T] {
      return array
   }

   return nil
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.