Intersect Array of object in iOS swift with efficiency and without using for loop

I want to Intersect two arrays that contains "People" objects and array size could be more then 10000 for each array and i want to intersect using "id" of objects without using for loop. i have already used for loop my solution but speed is slow. Some People objects can have same "id" in both array and some objects can have different "id"'s.

Below is sample example of my code

Example :

class People {

    var id:Int!

    var name:String!

}

let object1 = People(10, John)

let object2 = People(5, Rocky)

let array1 = [object1, object2, .... , object10000]

let array2 = [objectA1, objectA2, .... , objectA10000]

Upvotes: 2

Views: 3349

Answers (4)

OOPer
OOPer

Reputation: 47886

Assuming you want an Array of People, which is made from array2 by take out objects those are not present in array1.

Then just 2 lines:

let idSet1 = Set(array1.lazy.map{$0.id})
let interSection = array2.filter{idSet1.contains($0.id)}

Testing code:

import Foundation

class People {
    var id: Int
    var name: String

    init(id: Int, name: String) {
        self.id = id
        self.name = name
    }
}

do {
    //Generate random testing data
    let array1: [People] = (0..<20000).filter{_ in Int.random(in: 0...1) == 0}.map{People(id: $0, name: "P($0)")}
    print(array1.count)
    let array2: [People] = (0..<20000).filter{_ in Int.random(in: 0...1) == 0}.map{People(id: $0, name: "P($0)")}
    print(array2.count)

    let start = Date()
    let idSet1 = Set(array1.lazy.map{$0.id})
    let interSection = array2.filter{idSet1.contains($0.id)}
    let end = Date()
    print(interSection.count, end.timeIntervalSince(start))
}

Upvotes: 0

Nikdemm
Nikdemm

Reputation: 637

You can create Set with PeopleIds and Set with Object Id, something like this:

let peoples = ...
let objects = ...

let peoplesIds = peoples.map { $0.id }
let objectsIds = objects.map { $0.id }

let peoplesIdsSet = Set(peoplesIds)
let objectsIdsSet= Set(objectsIds)

let intersectionsIds = Array(peoplesIdsSet.intersection(objectsIdsSet))

Also I advise you to put it work in background thread, something like this:

DispatchQueue.global(qos: .background).async {

    findIntersecionsIds()

    DispatchQueue.main.async {
        // done
    }
}

Upvotes: 4

vacawama
vacawama

Reputation: 154583

The best way to perform an intersection is to use a Set. In order to add your items to a set, they need to be Hashable. Here is an implementation of your People class that is Hashable, and CustomStringConvertible (so that is can be printed nicely):

class People: Hashable, CustomStringConvertible {

    var id: Int
    var name: String

    var hashValue: Int { return id }
    var description: String { return "People(name: \(name), id: \(id)" }


    init(id: Int, name: String) {
        self.id = id
        self.name = name
    }

    // In order to conform to Hashable, you also need to be Equatable
    // Here we define equality as having the same id        
    static func ==(lhs: People, rhs: People) -> Bool {
        return lhs.id == rhs.id
    }
}

let object1 = People(id: 10, name: "John")
let object2 = People(id: 5, name: "Rocky")

let array1 = [object1, object2]

let array2 = [People(id: 10, name: "John"), People(id: 7, name: "Jane")]

let intersection = Array(Set(array1).intersection(array2))
print(intersection)
[People(name: John, id: 10]

Upvotes: 1

Prashant Tukadiya
Prashant Tukadiya

Reputation: 16446

Here is simple example

Suppose your struct is

struct User:Hashable {
    let id:Int
}

and your array

let array1 = [User(id:1),User(id:2),User(id:3),User(id:4),User(id:5)]
let array2 = [User(id:1),User(id:20),User(id:3),User(id:4),User(id:5)]

Now you can create set

let set1:Set<Int> = Set(array1.map{$0.id})
let set2:Set<Int> = Set(array2.map{$0.id})

and

let unique = set1.intersection(set2)
let elements = (array1 + array2).filter{unique.contains($0.id)}
print(elements)

Hope it is helpful

Upvotes: 1

Related Questions