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


443

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

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

Ответы:


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

Для забав я добавил еще один пример, где я расширяю встроенную 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"]"

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)

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.   
}


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)

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.


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


2

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

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

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

let indexOfA = find(testArray, "A") 
let indexOfB = find(testArray, "B")
let indexOfC = find(testArray, "C")

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

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


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

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

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 })
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.