Как найти индекс элемента списка в Swift?

443

Я пытаюсь найти item indexпутем поиска list. Кто-нибудь знает, как это сделать?

Я вижу , есть list.StartIndexи , list.EndIndexно я хочу что - то вроде Питона list.index("text").

Chéyo
источник

Ответы:

824

Поскольку swift в некоторых отношениях более функциональный, чем объектно-ориентированный (и массивы являются структурами, а не объектами), используйте функцию «find» для работы с массивом, которая возвращает необязательное значение, поэтому будьте готовы обработать нулевое значение:

let arr:Array = ["a","b","c"]
find(arr, "c")!              // 2
find(arr, "d")               // nil

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

Старая findфункция больше не поддерживается в Swift 2.0!

С Swift 2.0 Arrayполучает возможность находить индекс элемента, используя функцию, определенную в расширении CollectionType(которое Arrayреализует):

let arr = ["a","b","c"]

let indexOfA = arr.indexOf("a") // 0
let indexOfB = arr.indexOf("b") // 1
let indexOfD = arr.indexOf("d") // nil

Кроме того, поиск первого элемента в массиве, удовлетворяющего предикату, поддерживается другим расширением CollectionType:

let arr2 = [1,2,3,4,5,6,7,8,9,10]
let indexOfFirstGreaterThanFive = arr2.indexOf({$0 > 5}) // 5
let indexOfFirstGreaterThanOneHundred = arr2.indexOf({$0 > 100}) // nil

Обратите внимание, что эти две функции возвращают необязательные значения, как findи раньше.

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

Обратите внимание, что синтаксис indexOf изменился. Для предметов, соответствующих Equatableвам, можно использовать:

let indexOfA = arr.index(of: "a")

Подробную документацию о методе можно найти по адресу https://developer.apple.com/reference/swift/array/1689674-index.

Для элементов массива, которые не соответствуют, Equatableвам нужно использовать index(where:):

let index = cells.index(where: { (item) -> Bool in
  item.foo == 42 // test if this is the item you're looking for
})

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

С Swift 4.2 indexбольше не используется, но разделен на firstIndexи lastIndexдля лучшего разъяснения. Таким образом, в зависимости от того, ищете ли вы первый или последний индекс элемента:

let arr = ["a","b","c","a"]

let indexOfA = arr.firstIndex(of: "a") // 0
let indexOfB = arr.lastIndex(of: "a") // 3
Себастьян Шут
источник
22
Где вы узнали о функции поиска? Кажется, я не могу найти документацию по «find» или другим глобальным функциям
Johannes
14
Исходя из мира ООП, как я могу найти этот вид свободно плавающих функций?
Рудольф Адамкович
4
Они кажутся в значительной степени недокументированными. См. Practicalswift.com/2014/06/14/… для списка (но знайте, что я не проверял, является ли список полным или актуальным).
Себастьян Шут,
5
Йоханнес и @Rudolf - используйте Dash.app. Это приложение OS X для просмотра документации. В нем есть справочник по языку Swift, в котором есть список всех свободно плавающих функций. Легко фильтровать и искать. Не могу жить без этого.
Бьорн
4
К вашему сведению: если вы используете indexOfмассив структур, которые вы определили сами, ваша структура должна соответствовать Equatableпротоколу.
Мэтью Кирос
202

ТЛ; др:

Для классов вы можете искать:

let index = someArray.firstIndex{$0 === someObject}

Полный ответ:

Я думаю, что стоит упомянуть, что со ссылочными типами ( class) вы можете выполнить сравнение идентификаторов , и в этом случае вам просто нужно использовать ===оператор идентификации в закрытии предиката:


Swift 5, Swift 4.2:

let person1 = Person(name: "John")
let person2 = Person(name: "Sue")
let person3 = Person(name: "Maria")
let person4 = Person(name: "Loner")

let people = [person1, person2, person3]

let indexOfPerson1 = people.firstIndex{$0 === person1} // 0
let indexOfPerson2 = people.firstIndex{$0 === person2} // 1
let indexOfPerson3 = people.firstIndex{$0 === person3} // 2
let indexOfPerson4 = people.firstIndex{$0 === person4} // nil

Обратите внимание, что приведенный выше синтаксис использует синтаксис замыкающих замыканий и эквивалентен:

let indexOfPerson1 = people.firstIndex(where: {$0 === person1})


Swift 4 / Swift 3 - функция, которая раньше вызывалась index

Swift 2 - функция, которая раньше вызывалась indexOf

* Обратите внимание на актуальный и полезный комментарий по paulbailey о classтипах , которые реализуют Equatable, где вы должны рассмотреть , следует ли вам сравнение с использованием ===( тождественный оператора) или ==( равенство оператора). Если вы решите сопоставить, используя ==, то вы можете просто использовать метод, предложенный другими ( people.firstIndex(of: person1)).

Николай Суванджиев
источник
6
Это полезный пост для тех, кто интересуется, почему .indexOf (x) не работает - это решение неожиданно, но совершенно очевидно в ретроспективе.
Мори Марковиц
1
Большое спасибо за это, но для меня это не очевидно. Я посмотрел документацию и действительно не понимаю, зачем мне закрытие предиката при использовании indexOf для ссылочного типа? Такое ощущение, что indexOf уже должен иметь возможность обрабатывать ссылочные типы самостоятельно. Следует знать, что это ссылочный тип, а не тип значения.
Крис
7
Если Personреализован Equatableпротокол, это не будет необходимо.
paulbailey
2
Я получаю: Binary operator '===' cannot be applied to operands of type '_' and 'Post', Postмоя структура ... какие - либо идеи?
Дэвид Сик
@DavidSeek, structsenums) являются типами значений, а не ссылочными типами. Только ссылочные типы (например class) имеют логику сравнения идентичности ( ===). Посмотрите другие ответы о том, что делать structs(в основном вы просто используете array.index(of: myStruct), убедившись, что тип myStructсоответствует Equatable( ==)).
Николай Суванджиев
81

Вы можете filterмассив с замыканием:

var myList = [1, 2, 3, 4]
var filtered = myList.filter { $0 == 3 }  // <= returns [3]

И вы можете посчитать массив:

filtered.count // <= returns 1

Таким образом, вы можете определить , включает ли массив ваш элемент, комбинируя эти:

myList.filter { $0 == 3 }.count > 0  // <= returns true if the array includes 3

Если вы хотите найти позицию, я не вижу причудливого пути, но вы, безусловно, можете сделать это так:

var found: Int?  // <= will hold the index if it was found, or else will be nil
for i in (0..x.count) {
    if x[i] == 3 {
        found = i
    }
}

РЕДАКТИРОВАТЬ

В то время как мы на это, для удовольствия упражнения давайте расширим Arrayиметь findметод:

extension Array {
    func find(includedElement: T -> Bool) -> Int? {
        for (idx, element) in enumerate(self) {
            if includedElement(element) {
                return idx
            }
        }
        return nil
    }
}

Теперь мы можем сделать это:

myList.find { $0 == 3 }
// returns the index position of 3 or nil if not found
gwcoffey
источник
Для забав я добавил еще один пример, где я расширяю встроенную Arrayфункцию, чтобы иметь findметод, который делает то, что вы хотите. Я еще не знаю, если это хорошая практика, но это аккуратный эксперимент.
gwcoffey
2
Просто хочу отметить, что вы должны использовать ++ idx в соответствии с документацией: «Если вам не требуется специфическое поведение i ++, рекомендуется использовать ++ i и --i во всех случаях, потому что они имеют типичный ожидаемый поведение изменения я и возврата результата. "
Логан
Хороший звонок. Когда вы публиковали это, я пересматривал его, enumerateчтобы он больше не применялся , но вы абсолютно правы.
gwcoffey
Да, я вспомнил, что заметил это в одном из примеров, когда проходил. Пытаюсь сейчас привыкнуть :)
Логан
4
Чтобы это работало в Swift 2 / XCode 7, вам нужно изменить его следующим образом. Замените (включенный элемент: T -> Bool) на (включенный элемент: элемент -> Bool) и измените перечисление (self) на self.enumerate
Scooter
35

Swift 5

func firstIndex(of element: Element) -> Int?

var alphabets = ["A", "B", "E", "D"]

Example1

let index = alphabets.firstIndex(where: {$0 == "A"})

Example2

if let i = alphabets.firstIndex(of: "E") {
    alphabets[i] = "C" // i is the index
}
print(alphabets)
// Prints "["A", "B", "C", "D"]"
Saranjith
источник
25

Хотя indexOf()работает отлично, он возвращает только один индекс.

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

Вот как это можно сделать:

Свифт 3:

let array = ["apple", "dog", "log"]

let indexes = array.enumerated().filter {
    $0.element.contains("og")
    }.map{$0.offset}

print(indexes)

Свифт 2:

let array = ["apple", "dog", "log"]

let indexes = array.enumerate().filter {
    $0.element.containsString("og")
    }.map{$0.index}

print(indexes)
Сергей Яковенко
источник
12

Для пользовательского класса необходимо реализовать протокол Equatable.

import Foundation

func ==(l: MyClass, r: MyClass) -> Bool {
  return l.id == r.id
}

class MyClass: Equtable {
    init(id: String) {
        self.msgID = id
    }

    let msgID: String
}

let item = MyClass(3)
let itemList = [MyClass(1), MyClass(2), item]
let idx = itemList.indexOf(item)

printl(idx)
ZYiOS
источник
9

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

sequence.contains (element) : возвращает true, если заданная последовательность (например, массив) содержит указанный элемент.

Свифт 1:

Если вы просто хотите проверить, содержится ли элемент в массиве, то есть просто получите логический индикатор, используйте contains(sequence, element)вместо find(array, element):

Содержит (sequence, element) : Возвращает true, если заданная последовательность (например, массив) содержит указанный элемент.

Смотрите пример ниже:

var languages = ["Swift", "Objective-C"]
contains(languages, "Swift") == true
contains(languages, "Java") == false
contains([29, 85, 42, 96, 75], 42) == true
if (contains(languages, "Swift")) {
  // Use contains in these cases, instead of find.   
}
Zorayr
источник
7

в Swift 4.2

.index (где :) был изменен на .firstIndex (где :)

array.firstIndex(where: {$0 == "person1"})
Ридо Октанио
источник
6

Swift 4. Если ваш массив содержит элементы типа [String: AnyObject]. Таким образом, чтобы найти индекс элемента, используйте следующий код

var array = [[String: AnyObject]]()// Save your data in array
let objectAtZero = array[0] // get first object
let index = (self.array as NSArray).index(of: objectAtZero)

Или если вы хотите найти индекс на основе ключа из словаря. Здесь массив содержит объекты класса Model, и я сопоставляю свойство id.

   let userId = 20
    if let index = array.index(where: { (dict) -> Bool in
           return dict.id == userId // Will found index of matched id
    }) {
    print("Index found")
    }
OR
      let storeId = Int(surveyCurrent.store_id) // Accessing model key value
      indexArrUpTo = self.arrEarnUpTo.index { Int($0.store_id) == storeId }! // Array contains models and finding specific one
Гурджиндер Сингх
источник
4

Swift 2.1

var array = ["0","1","2","3"]

if let index = array.indexOf("1") {
   array.removeAtIndex(index)
}

print(array) // ["0","2","3"]

Свифт 3

var array = ["0","1","2","3"]

if let index = array.index(of: "1") {
    array.remove(at: index)
}
array.remove(at: 1)
Лука Даванцо
источник
3
Вы мутируете let array? Использование selfсомнительно также.
Чейо
4

В Swift 4 , если вы просматриваете свой массив DataModel, убедитесь, что ваша модель данных соответствует Equatable Protocol, реализуйте метод lhs = rhs и только тогда вы можете использовать «.index (of». Например,

class Photo : Equatable{
    var imageURL: URL?
    init(imageURL: URL){
        self.imageURL = imageURL
    }

    static func == (lhs: Photo, rhs: Photo) -> Bool{
        return lhs.imageURL == rhs.imageURL
    }
}

А потом,

let index = self.photos.index(of: aPhoto)
Naishta
источник
4

В Swift 2 (с Xcode 7) Arrayвключает indexOfметод, предоставляемый CollectionTypeпротоколом. (На самом деле, два indexOfметода - один, который использует равенство для сопоставления аргумента, и другой, который использует замыкание.)

До Swift 2 у родовых типов, таких как коллекции, не было способа предоставить методы для конкретных производных от них типов (например, массивов). Итак, в Swift 1.x «index of» является глобальной функцией ... И она также переименована, так что в Swift 1.x эта глобальная функция вызывается find.

Также возможно (но не обязательно) использовать indexOfObjectметод из NSArray... или любого другого, более сложного метода поиска из Foundation, который не имеет эквивалентов в стандартной библиотеке Swift. Просто import Foundation(или другой модуль, который транзитивно импортирует Foundation), приведите Arrayк нему NSArray, и вы сможете использовать множество методов поиска NSArray.

rickster
источник
4

Любое из этого решения работает для меня

Это решение у меня есть для Swift 4:

let monday = Day(name: "M")
let tuesday = Day(name: "T")
let friday = Day(name: "F")

let days = [monday, tuesday, friday]

let index = days.index(where: { 
            //important to test with === to be sure it's the same object reference
            $0 === tuesday
        })
Кевин ABRIOUX
источник
2

Вы также можете использовать функциональную библиотеку Dollar для индексации массива как такового http://www.dollarswift.org/#indexof-indexof

$.indexOf([1, 2, 3, 1, 2, 3], value: 2) 
=> 1
Encore PTL
источник
2

Если вы все еще работаете в Swift 1.x

тогда попробуй,

let testArray = ["A","B","C"]

let indexOfA = find(testArray, "A") 
let indexOfB = find(testArray, "B")
let indexOfC = find(testArray, "C")
iCyberPaul
источник
1

Для SWIFT 3 вы можете использовать простую функцию

func find(objecToFind: String?) -> Int? {
   for i in 0...arrayName.count {
      if arrayName[i] == objectToFind {
         return i
      }
   }
return nil
}

Это даст номер позиции, так что вы можете использовать как

arrayName.remove(at: (find(objecToFind))!)

Надеюсь быть полезным

Marco
источник
1

SWIFT 4

Допустим, вы хотите сохранить число из массива с именем cardButtons в cardNumber, вы можете сделать это следующим образом:

let cardNumber = cardButtons.index(of: sender)

отправитель имя вашей кнопки


источник
0

Swift 4

Для справочных типов:

extension Array where Array.Element: AnyObject {

    func index(ofElement element: Element) -> Int? {
        for (currentIndex, currentElement) in self.enumerated() {
            if currentElement === element {
                return currentIndex
            }
        }
        return nil
    }
}
Менно
источник
0

В Swift 4/5 используйте «firstIndex» для поиска индекса.

let index = array.firstIndex{$0 == value}
Крунал Патель
источник
0

В случае, если у кого-то есть эта проблема

Cannot invoke initializer for type 'Int' with an argument list of type '(Array<Element>.Index?)'

Просто сделай это

extension Int {
    var toInt: Int {
        return self
    }
}

тогда

guard let finalIndex = index?.toInt else {
    return false
}
Ахмед Сафади
источник
0

За (>= swift 4.0)

Это довольно просто. Рассмотрим следующий Arrayобъект.

var names: [String] = ["jack", "rose", "jill"]

Чтобы получить индекс элемента rose, все, что вам нужно сделать, это:

names.index(of: "rose") // returns 1

Замечания:

  • Array.index(of:)возвращает Optional<Int>.

  • nil подразумевает, что элемент не присутствует в массиве.

  • Возможно, вы захотите принудительно развернуть возвращаемое значение или использовать, if-letчтобы обойти необязательное.

Мохаммед Садик
источник
0

Просто используйте метод firstIndex.

array.firstIndex(where: { $0 == searchedItem })
erdikanik
источник