jamryu
jamryu

Reputation: 698

How to get index(of:) to return multiple indices?

Here is a group array.

var group = ["H","H","E","D",
             "G","D","G","E",
             "D","B","A","B",
             "A","A","G","C",
             "C","H","D","G",
             "H","B","E","F",
             "F","C","E","A",
             "B","C","F","F"]

I want to do something like this to find indices of "A".

group.index(of: "A"!)

But this will return only first index, but not other indices for next three "A"s.

print(group.index(of: "A")!) //10

What do I do to get the program to return all four indices for "A"?

Upvotes: 7

Views: 4101

Answers (8)

Leo Dabus
Leo Dabus

Reputation: 236370

You can extend collection and create your own indices method that takes a predicate.

extension Collection where Element: Equatable {
    func indices(where predicate: @escaping (Element) throws -> Bool) rethrows -> [Index] {
        try self.indices.filter({try predicate(self[$0])})
    }
    func indices(of element: Element) -> [Index] {
        self.indices.filter({self[$0] == element})
    }
}

For collections indexed by Int you can return an IndexSet:

extension Collection where Element: Equatable, Index == Int {
    func indexSet(where predicate: @escaping (Element) throws -> Bool) rethrows -> IndexSet {
        .init(try indices(where: predicate))
    }
    func indexSet(of element: Element) -> [Index] {
        .init(indices(of: element))
    }
}

Usage:

let array = [10, 20, 30, 5, 15, 25, 50]
let indices = array.indices { $0.isMultiple(of: 3) }

for index in indices {
    print("element:", array[index], "at:", index)
}

This will print

element: 30 at: 2

element: 15 at: 4


let group = ["H","H","E","D","G","D","G","E","D","B","A","B","A","A","G","C","C","H","D","G","H","B","E","F","F","C","E","A","B","C","F","F"]
let indices = group.indices(of: "A")
print(indices) 

This will print

[10, 12, 13, 27]

Upvotes: 0

jamryu
jamryu

Reputation: 698

After reading about Extensions and functional programming past few days, I wanted to try writing an extension function for Array class. Here it is:

var group = ["H","H","E","D",
             "G","D","G","E",
             "D","B","A","B",
             "A","A","G","C",
             "C","H","D","G",
             "H","B","E","F",
             "F","C","E","A",
             "B","C","F","F"]

var group2 = [1,2,3,4,5,6,7,8]

// Create a nice Array extension method that will return indices of wanted group.

extension Array where Element: Equatable {

func showIndices(indexOf groupName: Element) -> [Int] {
    return self.enumerated().compactMap {
        $0.element == groupName ? $0.offset : nil
    }
}
}

group.showIndices(indexOf: "A")
group2.showIndices(indexOf: 1)

// [10, 12, 13, 27]
// [0]

Upvotes: 0

mugx
mugx

Reputation: 10105

You might use a combination of enumerated and compactMap:

let indexArray = group.enumerated().compactMap {
   $0.element == "A" ? $0.offset : nil
}    
print(indexArray) // [10, 12, 13, 27]

Upvotes: 13

Cristik
Cristik

Reputation: 32809

A reduce, generic, based solution:

let group = ["H","H","E","D",
             "G","D","G","E",
             "D","B","A","B",
             "A","A","G","C",
             "C","H","D","G",
             "H","B","E","F",
             "F","C","E","A",
             "B","C","F","F"]

extension Array where Element: Equatable {
    func indexes(of element: Element) -> [Index] {
        return enumerated().reduce([]) { $1.element == element ? $0 + [$1.offset] : $0 }
    }
}

group.indexes(of: "A") // 10, 12, 13, 27]

Another approach would be to built-up a letter-to-indices dictionary and query that:

let indices = Dictionary(group.enumerated().map { ($1, [$0]) }, uniquingKeysWith: +)
indices["A"] ?? [] // [10, 12, 13, 27]

, though for this solution you'd need to unwrap the result as the dictionary subscript returns an optional value.

BTW, your array could've been a let instead of a var, constants bring predictability over your code, I recommend using them as much as possible.

Upvotes: 1

staticVoidMan
staticVoidMan

Reputation: 20244

Vadian's answer above is already the most concise yet.

The rest of the answers just dance about chained HUFs because it's cool but just for true completeness, as the most basic implementation one would obviously do the following:

let group = ["H","H","E","D",
             "G","D","G","E",
             "D","B","A","B",
             "A","A","G","C",
             "C","H","D","G",
             "H","B","E","F",
             "F","C","E","A",
             "B","C","F","F"]

var array = [Int]()
for index in 0..<group.count {
    if group[index] == "A" {
        array.append(index)
    }
}

Upvotes: 1

vadian
vadian

Reputation: 285072

For the sake of completeness this is another different approach which filters the indices

let group = ["H","H","E","D",
             "G","D","G","E",
             "D","B","A","B",
             "A","A","G","C",
             "C","H","D","G",
             "H","B","E","F",
             "F","C","E","A",
             "B","C","F","F"]

let indices = group.indices.filter{ group[$0] == "A"}

Upvotes: 2

Alain T.
Alain T.

Reputation: 42143

Or just enumerate/filter/map :

 group.enumerated().filter{$1=="A"}.map{$0.offset}

[EDIT] Changed $0.0 to $0.offset as per Alexander's recommendation (makes the code more legible/explicit)

Upvotes: 8

Reinier Melian
Reinier Melian

Reputation: 20804

You can use a simple cycle

Use this Code

var group = ["H","H","E","D",
             "G","D","G","E",
             "D","B","A","B",
             "A","A","G","C",
             "C","H","D","G",
             "H","B","E","F",
             "F","C","E","A",
             "B","C","F","F"]
var indexes : [Int] = []
for (index,string) in group.enumerated() {
    if(string == "A") {
        indexes.append(index)
    }
}

debugPrint(indexes)

Upvotes: 4

Related Questions