7

Given an array of indexes I would like to get a subarray of myArray, with the items at those indexes. I'm currently iterating the indexes array to create a subarray, but I'm wondering if this can be achieved by using the .filter function.

var indexes = [3,4,9,11]
myArray.filter(...)
1
  • filter would be inefficient because it would look at each value in myArray. Consider what would happen if myArray had 100 thousand items. Your method of iterating the indexes array would loop 4 times, but filter would loop (internally) 100 thousand times. Commented Jul 4, 2016 at 10:04

3 Answers 3

16

Assuming that

  • the given indices are in increasing order, and
  • all indices are valid for the array (i.e. less than the number of elements),

then a simple map operation would work:

let indexes = [2, 4, 7]
let myArray = ["a", "b", "c", "d", "e", "f", "g", "h"]

let filtered = indexes.map { myArray[$0] }
print(filtered) //["c", "e", "h"]

Remark: In earlier Swift releases, there was a PermutationGenerator exactly for this purpose:

let filtered = Array(PermutationGenerator(elements: myArray, indices: indexes))
print(filtered) //["c", "e", "h"]

However, this has been deprecated in Swift 2.2 and will be removed in Swift 3. I haven't seen a Swift 3 replacement yet.

Sign up to request clarification or add additional context in comments.

4 Comments

mhm this sounds cool. The 2 requirements are always satisfied. Is this solution more efficient than iterating?
@Patrick: You should test it. I am pretty sure that map() iterates internally as well. – Possible advantages compared to an explicit loop are: Shorter functional code, and the result is a constant.
Thanks for the answer. Could you please explain why the first assumption is required "the given indices are in increasing order" ? I can't see a reason why it won't work even if this assumption is broken. Thank you.
@NikolaySuvandzhiev: I assumed that the intension is to get a “subarray” of elements in their original order (as with the filter method). If the order does not matter then that restriction is not needed.
0

you can try, although that's far away from efficient. But that's what you get if you insist on using filter

let myArray = ["a", "b", "c", "d", "e"]
var indexes = [1, 3]
myArray.filter { (vowel) -> Bool in
    if let index = myArray.indexOf(vowel) {
        return indexes.contains(index)
    }
    return false
}

4 Comments

Do you think that this is more efficient than an iteration?
noway :D I couldn't think of any efficient way to do it in filter
I see, ok then best way is to iterate, I guess.
There may be unexpected results if the array elements are not unique (since indexOf returns the index of the first occurrence).
0

You can also use flatMap to retrieve the filtered subArray

let indexes = [2, 4, 7]
let myArray = ["a", "b", "c", "d", "e", "f", "g", "h"]

let subArray = indexes.flatMap { (index) -> String? in
    return (0 <= index && index < myArray.count) ? myArray[index] : nil
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.