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

368

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

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

Теги:
arrays

16 ответов

671
Лучший ответ

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

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
  • 21
    Где вы узнали о функции поиска? Я не могу найти какую-либо документацию "найти" или любые другие глобальные функции
  • 71
    Просто введите импорт Swift и Ctrl-Click Swift
Показать ещё 16 комментариев
141

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


Свифт 4/Свифт 3:

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.index{$0 === person1} // 0
let indexOfPerson2 = people.index{$0 === person2} // 1
let indexOfPerson3 = people.index{$0 === person3} // 2
let indexOfPerson4 = people.index{$0 === person4} // nil

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

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


Swift 2 - синтаксис функции index:

let indexOfPerson1 = people.indexOf{$0 === person1}


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

  • 5
    Это полезный пост для тех, кто интересуется, почему .indexOf (x) не работает - это решение неожиданно, но совершенно очевидно в ретроспективе.
  • 1
    Большое спасибо за это, но для меня это совершенно не очевидно. Я посмотрел документацию и действительно не понимаю, зачем мне закрытие предиката при использовании indexOf для ссылочного типа? Такое ощущение, что indexOf уже должен иметь возможность обрабатывать ссылочные типы самостоятельно. Следует знать, что это ссылочный тип, а не тип значения.
Показать ещё 4 комментария
74

Вы можете 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
  • 0
    Спасибо за работу вокруг.
  • 0
    Для забавы я добавил еще один пример, где я расширяю встроенный Array чтобы иметь метод find который делает то, что вы хотите. Я пока не знаю, хорошая ли это практика, но это аккуратный эксперимент.
Показать ещё 6 комментариев
21

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

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

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

Swift 3:

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

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

print(indexes)

Swift 2:

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

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

print(indexes)
13

Swift 4.2

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

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

Example1

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

Example2

if let i = alphabets.index(of: "E") {
    alphabets[i] = "C" // i is the index
}
print(alphabets)
// Prints "["A", "B", "C", "D"]"
  • 1
    Просто и мощно!
  • 1
    Потрясающие! Спасибо..
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, если заданная последовательность (например, массив) содержит указанный элемент.

Swift 1:

Если вы хотите просто проверить, содержится ли элемент внутри массива, то есть просто получить логический индикатор, используйте contains(sequence, element) вместо find(array, 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.   
}
5

В 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), NSArray свой Array в NSArray, и вы можете использовать множество методов поиска в NSArray.

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

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

Это решение для 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 the same object reference
            $0 === tuesday
        })
  • 0
    Решение LEGIT 4.2 готово!
2

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

Swift 2.1

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

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

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

Swift 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 также сомнительно.
  • 0
    Как раз то, что мне было нужно. Спасибо @Velthune
2

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

затем попробуйте

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

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

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

$.indexOf([1, 2, 3, 1, 2, 3], value: 2) 
=> 1
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 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))!)

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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню