Как проверить, находится ли элемент в массиве

Как в Swift проверить, существует ли элемент в массиве? Xcode не предлагает никаких предложений для contain, include или has, и быстрый поиск по книге ничего не дал. Есть идеи, как это проверить? Я знаю, что есть метод find, который возвращает номер индекса, но есть ли метод, который возвращает логическое значение, например #include? ruby?

Пример того, что мне нужно:

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

person jaredsmith    schedule 08.06.2014    source источник
comment
if find(elements, 5) != nil { } недостаточно хорош?   -  person Martin R    schedule 08.06.2014
comment
Я надеялся на что-то еще более чистое, но это выглядело не очень хорошо. Я еще ничего не нашел ни в документации, ни в книге.   -  person jaredsmith    schedule 08.06.2014


Ответы (17)


Swift 2, 3, 4, 5:

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

contains() - это метод расширения протокола для SequenceType (для последовательностей Equatable elements), а не глобальный метод, как в более ранних версиях.

Примечания:

Предыдущие версии Swift:

let elements = [1,2,3,4,5]
if contains(elements, 5) {
    println("yes")
}
person Martin R    schedule 19.08.2014
comment
Любая документация о таких глобальных функциях? - person Rivera; 14.04.2015
comment
Это должно работать, если каждый элемент внутри массива (и элемент, который мы ищем) имеет тип Dictionary ‹String, AnyObject›? Пытаюсь добиться этого, но получаю ошибку времени компиляции. - person ppalancica; 30.04.2015
comment
@ppalancica: для этого требуется, чтобы элементы массива соответствовали протоколу EquatableDictionary<String, AnyObject> не соответствует). Существует второй вариант contains(), который принимает предикат (сравните stackoverflow.com/questions/29679486/), возможно, вы можете использовать это, например if contains(array, { $0 == dict } ) ... - person Martin R; 30.04.2015
comment
Как искать конкретный элемент из общего массива? скажи [AnyObject]? - person Dhaval H. Nena; 07.07.2016

Для тех, кто пришел сюда в поисках найти и удалить объект из массива:

Swift 1

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

Swift 2

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

Swift 3, 4

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

Swift 5.2

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

Используйте это расширение:

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
person David Berry    schedule 08.06.2014
comment
в зависимости от того, к чему вы привыкли, .contains может казаться более интуитивным и запоминающимся. - person Pirijan; 27.07.2014
comment
Не могли бы вы объяснить свой синтаксис, разбив его на части? Я никогда раньше не видел такого форматирования, и у вас сразу происходит много сложных вещей! - person Aggressor; 26.12.2014

Если вы проверяете, содержится ли экземпляр настраиваемого класса или структуры в массиве, вам необходимо реализовать протокол Equatable, прежде чем вы сможете использовать .contains (myObject ).

Например:

struct Cup: Equatable {
    let filled:Bool
}

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

тогда вы можете сделать:

cupArray.contains(myCup)

Совет: переопределение == должно быть на глобальном уровне, а не в вашем классе / структуре.

person Andrew Schreiber    schedule 04.11.2015

Я использовал фильтр.

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
}
person Jeffery Thomas    schedule 08.06.2014
comment
Мне нравится решение с фильтром, потому что вы можете использовать его для самых разных целей. Например, я портировал некоторый код, который зацикливался и зацикливался, пытаясь увидеть, есть ли в списке уже элемент с одним из его полей, содержащим строковое значение. В Swift это одна строка с использованием фильтра в этом поле. - person Maury Markowitz; 08.03.2015
comment
filter неэффективен, потому что он всегда перебирает все элементы вместо того, чтобы немедленно возвращаться, когда элемент найден. Вместо этого лучше использовать find (). - person Thorsten; 19.04.2015

(Swift 3)

Проверьте, существует ли элемент в массиве (удовлетворяющий некоторым критериям), , и если да, продолжайте работу с первым таким элементом

Если намерение:

  1. Чтобы проверить, существует ли элемент в массиве (/ удовлетворяет некоторым логическим критериям, не обязательно проверке равенства),
  2. И если это так, продолжайте работу с первым таким элементом,

Тогда альтернативой contains(_:) в соответствии с планом Sequence является _ 3_ из 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:) _ 12_ (который легко доступен для коллекции массивов) может быть используется для поиска индекса первого элемента, выполняющего предикат, после чего индекс можно использовать с 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 _ 17_ для условного (для .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:) с предикатом уместно (поскольку мы больше не проверяем равенство, а выполняем предоставленный предикат).

person dfrib    schedule 01.11.2016
comment
Как удалить firstSuchElement из массива? - person i6x86; 06.02.2017
comment
@ i6x86 спасибо за вопрос. Я обновил свой ответ примером того, как удалить элемент (а также как удалить и зафиксировать удаленный элемент). - person dfrib; 06.02.2017

Самый простой способ добиться этого - использовать фильтр для массива.

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

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

if result.isEmpty {
    // element does not exist in array
} else {
    // element exists
}
person davetw12    schedule 01.09.2015
comment
отличное решение. поэтому этот метод возвращает массив. Однако я использую это для поиска идентификатора. В моем приложении d уникальны, поэтому результат может быть только один. Есть ли способ вернуть только 1 результат? Я пока использую результат [0] - person Dan Beaulieu; 01.10.2015
comment
@DanBeaulieu Выполнение чего-то вроде let result = elements.filter { $0==5 }.first должно выполнить то, что вы ищете. - person davetw12; 04.10.2015
comment
@ davetw12 Нет. Это приведет к ненужному повторению всей коллекции. Лучше использовать first(where: predicate). - person Leo Dabus; 14.12.2020

Swift 4/5

Другой способ добиться этого - использовать функцию фильтра.

var elements = [1,2,3,4,5]
if let object = elements.filter({ $0 == 5 }).first {
    print("found")
} else {
    print("not found")
}
person Pramod More    schedule 08.06.2018
comment
Это приведет к ненужному повторению всей коллекции. Это определенно неправильный подход. - person Leo Dabus; 14.12.2020

Массив, содержащий свойство, равное

yourArray.contains(where: {$0.propertyToCheck == value })

Возвращает логическое значение.

person Honcharov Anton    schedule 07.10.2020

Начиная с Swift 2.1 NSArrays имеет containsObject, который можно использовать так:

if myArray.containsObject(objectImCheckingFor){
    //myArray has the objectImCheckingFor
}
person ColossalChris    schedule 19.01.2016
comment
На самом деле это для NSArray. Не быстрый массив - person Tycho Pandelaar; 15.03.2016
comment
Да, но вы можете временно преобразовать свой быстрый массив в NSArray: если пусть tempNSArrayForChecking = mySwiftArray как NSArray? где tempNSArrayForChecking.containsObject (objectImCheckingFor) {// myArray имеет объект} - person Vitalii; 18.05.2016

Множество

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
person Sazzad Hissain Khan    schedule 22.05.2019

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

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

Вот мое небольшое расширение, которое я только что написал, чтобы проверить, содержит ли мой массив делегатов объект делегата (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
    }
}

Если у вас есть идея, как оптимизировать этот код, просто дайте мне знать.

person DevAndArtist    schedule 12.05.2015

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

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

 if arrelemnts.contains("test"){
    print("found")   }else{
    print("not found")   }
person Yogesh shelke    schedule 03.03.2016

Быстрый

Если вы не используете объект, вы можете использовать этот код для contains.

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")

 }
person Anit Kumar    schedule 02.08.2016

как насчет использования хеш-таблицы для работы, как это?

во-первых, создание универсальной функции «хэш-карты», расширяющей протокол последовательности.

extension Sequence where Element: Hashable {

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

Это расширение будет работать до тех пор, пока элементы в массиве соответствуют Hashable, например целые числа или строки, вот использование ...

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
person James Rochabrun    schedule 11.06.2018

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
}
person Kiran Jasvanee    schedule 03.04.2019