Як знайти індекс елемента списку в Swift?


443

Я намагаюся знайти item indexпошуковий запит list. Хтось знає, як це зробити?

Я бачу , є list.StartIndexі , list.EndIndexале я хочу що - щось на зразок Пітона list.index("text").

Відповіді:


823

Оскільки 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
Де ви дізналися про функцію знаходження? Я, здається, не можу знайти жодної документації "знайти" чи будь-яких інших глобальних функцій
Йоганнеса,

14
Як виходити зі світу OOP, як я повинен знайти цей вид вільних плаваючих функцій?
Рудольф Адамкович

4
Вони здаються значною мірою незадокументованими. Див. Практичний опис для переліку (але пам’ятайте, що я не перевіряв, чи список є повним чи оновленим).
Себастьян Шут

5
Йоханнес і @Rudolf - використовуйте Dash.app. Це додаток OS X для перегляду документації. У ньому є мовна довідка Swift, яка містить перелік усіх функцій вільного плавання. Легко фільтрувати та шукати. Не можу жити без цього.
Бьорн

4
FYI: Якщо ви використовуєте indexOfмасив структур, який ви самі визначили, ваша структура повинна відповідати Equatableпротоколу.
Меттью Кірос

202

tl; dr:

Для занять ви можете шукати:

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) не працює - це рішення є несподіваним, але абсолютно очевидним в ретроспективі.
Maury Markowitz

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

EDIT

Хоча ми це робимо, для цікавої вправи продовжимо, 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 у всіх випадках, оскільки вони мають типовий очікуваний поведінка модифікації i та повернення результату ".
Логан

Гарний дзвінок. Коли ви публікували це, я переглядав його для використання, enumerateщоб він більше не застосовувався, але ви абсолютно праві.
gwcoffey

Так, я згадав це, помітивши це в одному з прикладів, коли я переживав. Намагаюся налаштувати себе на звичку зараз :)
Логан

4
Щоб це працювало під Swift 2 / XCode 7, вам потрібно змінити його наступним чином. Замініть (включеноElement: T -> Bool) на (включеноElement: Element -> Bool) та змініть нумерацію (self) на self.enumerate
Scooter

35

Швидкий 5

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

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

Приклад1

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

Приклад2

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:

row.contains (елемент) : Повертає true, якщо задана послідовність (наприклад, масив) містить зазначений елемент.

Швидкий 1:

Якщо ви шукаєте просто перевірити, чи міститься елемент всередині масиву, тобто просто отримайте булевий індикатор, використовуйте contains(sequence, element)замість find(array, element):

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

Дивіться приклад нижче:

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

Швидкий 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

Швидкий 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також сумнівно.
Chéyo

4

У Swift 4 , якщо ви проїжджаєте через масив DataModel, переконайтеся, що ваша модель даних відповідає протоколу Equatable, застосуйте метод lhs = rhs і лише тоді ви можете використовувати ".index (з". Наприклад

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 "індекс" - це глобальна функція ... І він також був перейменований, тому в 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

Швидкий 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?)'

jsut зроби це

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.