0

If I have an array:

let array = [
        ["Hamburger", "Nachos", "Lasagne"],
        ["Tomatoes", "Apples", "Oranges"],
        ["Soda", "Juice", "Water"]
    ]    

What is the index of for example "Apples"? And is there a way to get it programmaticly?

Leo Dabus
  • 229,809
  • 59
  • 489
  • 571
Eccles
  • 394
  • 1
  • 3
  • 13

4 Answers4

5

You can use firstIndex(where:) and find the subindex of it using firstIndex(of:):

let array = [
    ["Hamburger", "Nachos", "Lasagne"],
    ["Tomatoes", "Apples", "Oranges"],
    ["Soda", "Juice", "Water"]
]

let query = "Apples"
if let index = array.firstIndex(where: {$0.contains(query)}),
    let subIndex = array[index].firstIndex(of: query) {
    print(array[index][subIndex])  // Apples

}

As an Extension:

extension Collection where Element: Collection, Element.Element: Equatable {
    func firstIndexAndSubIndex(of element: Element.Element) -> (index: Index, subIndex: Element.Index)? {
        if let index = firstIndex(where: {$0.contains(element)}),
            let subIndex = self[index].firstIndex(of: element) {
            return (index,subIndex)
        }
        return nil
    }
}

usage:

let array = [
    ["Hamburger", "Nachos", "Lasagne"],
    ["Tomatoes", "Apples", "Oranges"],
    ["Soda", "Juice", "Water"]
]
let query = "Soda"
if let indexes = array.firstIndexAndSubIndex(of: query) {
    print(indexes)   // "(index: 2, subIndex: 0)\n"
}

This would work also to find the index of a character from an array of strings:

let array = ["Hamburger", "Nachos", "Lasagne"]
let query: Character = "h"
if let indices = array.indexAndSubIndex(of: query) {
    print(indices)   // "(index: 1, subIndex: Swift.String.Index(_rawBits: 196865))\n"
    array[indices.index][indices.subIndex]  // "h"
}
Leo Dabus
  • 229,809
  • 59
  • 489
  • 571
0

Another option as an extension. The function tupleIndex(of:) returns a tuple of (Int, Int)?.

let array = [
    ["Hamburger", "Nachos", "Lasagne"],
    ["Tomatoes", "Apples", "Oranges"],
    ["Soda", "Juice", "Water"]
]

extension Collection where
    Element: Collection,
    Element.Element: Equatable,
    Element.Index == Int {

    func tupleIndex(of elementToFind: Element.Element) -> (Int, Int)? {
        for (firstIndex, element) in self.enumerated() {
            if let secondIndex = element.index(of: elementToFind) {
                return (firstIndex, secondIndex)
            }
        }
        return nil
    }
}

You can use it like this:

print(array.tupleIndex(of: "Apples")) //prints Optional((1, 1))
Yannick
  • 3,210
  • 1
  • 21
  • 30
  • In Swift 4 you can simplify the constraints, because Element == Iterator.Element. – Martin R Nov 03 '17 at 22:34
  • Better add a constraint Element.Index == Int instead of the conditional cast `as? Int`. – With your approach `["abc", "def"].tupleIndex(of: "b")` compiles, but returns nil. – Martin R Nov 03 '17 at 22:39
  • You are right. I missed that case. I wasn't happy with the conditional cast anyways, so thanks for the suggestion. I'll edit my answer. – Yannick Nov 03 '17 at 22:48
0

Its not elegant but simple to read

func getIndices(arr: [[String]], word: String) -> (Int, Int)? {
  for i in 0..<arr.count {
    let subArr = arr[i]
    if let index = subArr.index(of: word) {
        return (i, index)
    }
  }
  return nil
}
let result = getIndices(arr: array, word: "Apples"))
KimNguyen
  • 1,781
  • 1
  • 13
  • 5
-3
let array = [
        ["Hamburger", "Nachos", "Lasagne"],
        ["Tomatoes", "Apples", "Oranges"],
        ["Soda", "Juice", "Water"]
]

for arr in array {

  let answer = arr.indexOf("Apples")
  if answer {
    break
  }
print(answer)
}
Mike Tung
  • 4,735
  • 1
  • 17
  • 24