Reputation: 227
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
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
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
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
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