kubilay
kubilay

Reputation: 5330

Swift Arrays: Keeping the order while filtering an array using another

I have an array as a list of numbers. In another smaller array I hold some numbers (in a different order) and I'd like to filter my first array using the values in the second array.

My arrays:

let allNumbers = [50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160, 170, 180, 190, 200]
let someNumbers = [100, 90, 120, 200] // unordered

When I use filter method or loop through the values of the first array, I can get the correct values but the order of the elements is not same with my second array but the first -naturally.

Method 1: filter method, losing order

let filtered = allNumbers.filter { someNumbers.contains($0) } // Runs 17 times
print(filtered) // "[90, 100, 120, 200]" (wrong order)

Method 2: Looping through first array, losing order

var filteredWithLoop: [Int] = []
for number in allNumbers { // Runs 16 Times
    if someNumbers.contains(number) {
        filteredWithLoop.append(number)
    }
}
print(filteredWithLoop) // "[90, 100, 120, 200]" (wrong order)

Hack: I can work this around by filtering the first array when I'm looping through the second, like this:

var filteredUglyWay: [Int] = []
for number in someNumbers {
    if let alone = (allNumbers.filter { $0 == number }).first { // Runs 4 * 16 Times
        filteredUglyWay.append(alone)
    }
}
print(filteredUglyWay) // "[100, 90, 120, 200]" (correct order)

But this feels more like a hack rather than a solution to me especially considering that the loop-ish filter method is called in a loop.

Is there a better approach for this or is this just how it's supposed to be?

Important note: The first array is actually a representation to make the situation more understandable. In my real implementation, first array is holding some objects and the second array is just the ID list of some objects (like favorites of the user) so each time I fetch the whole data, I am trying to filter the favorite objects according to this ID list.

Upvotes: 1

Views: 3195

Answers (3)

user14247928
user14247928

Reputation: 1

You can do this:

let filtered = someNumbers.filter { allNumbers.contains($0) }
print(filtered) // Prints [100, 90, 120, 200]

It's probably less efficient when allNumbers is much larger than someNumbers, but if efficiency isn't an issue here, it seems like this is the most neat solution to me.

Upvotes: 0

Larme
Larme

Reputation: 26036

A possible solution: Filter, then sort.

let filtered = allNumbers.filtered({ someNumbers.contains($0) })
let filteredAndSorted = filtered.sorted(by: { someNumbers.index(of: $0)! < someNumbers.index(of: $1)! })

Side note, since filtered is composed of data only in someNumbers, the fore unwrap shouldn't create a crash.

Upvotes: 3

Bhavin Kansagara
Bhavin Kansagara

Reputation: 2916

why not to try updated method 2

Method 2 UPDATED: Looping through second array

var filteredWithLoop: [Int] = []
    for number in someNumbers { // Runs 4 Times
        if allNumbers.contains(number) {
            filteredWithLoop.append(number)
        } // IF OBJECT IS NOT THERE IN FIRST ARRAY IT WILL BE AUTOMATICALLY DISCARDED
    }
    print(filteredWithLoop) // "[100, 90, 120, 200]" (proper order)

Upvotes: 0

Related Questions