alionthego
alionthego

Reputation: 9743

filter swift array of objects to remove objects who have the same value in one or more of their properties

I have a Swift array of coreData objects. The objects have three properties. I would like to filter the array to remove objects that share the same value for 2 of these properties.

Object properties are object.name, object.age, object.id

All properties are of type String.

I want to filter the array to return an array removing duplicate cases where object.name AND object.age are the same.

Upvotes: 3

Views: 2669

Answers (3)

ramzesenok
ramzesenok

Reputation: 6911

Although Alain's solution is very nice, here's somewhat more swifty way which benefits from KeyPaths:

extension Array {
    func filterDuplicates(by keyPath: PartialKeyPath<Element>) -> Self {
        var uniqueKeys = Set<String>()

        return self.filter { uniqueKeys.insert("\($0[keyPath: keyPath])").inserted }
    }
}

Now you can use it like this: yourArr.filterDuplicates(by: \YourClass.propertyName)

And if you want to use multiple properties to determine uniqueness, here's the solution for that:

extension Array {
    func withRemovedDuplicates(by keyPaths: PartialKeyPath<Element>...) -> Self {
        var uniqueKeys = Set<String>()
        
        return self.filter { element in
            uniqueKeys.insert(keyPaths.map { "\(element[keyPath: $0])" }.joined()).inserted
        }
    }
}

Use is this way:

yourArr.filterDuplicates(by: \YourClass.propertyOneName, \YourClass.propertyTwoName)

Upvotes: 1

Alain T.
Alain T.

Reputation: 42143

You could do it with a set:

var uniqueValues = Set<String>()
objectsArray = objectsArray.filter{ uniqueValues.insert("\($0.name)|\($0.age)").inserted}

You could also generalize the approach to cover other combinations by extending the Array type:

extension Array
{
   func filterDuplicate<T>(_ keyValue:(Element)->T) -> [Element]
   {
      var uniqueKeys = Set<String>()
      return filter{uniqueKeys.insert("\(keyValue($0))").inserted}
   }
}

objectsArray = objectsArray.filterDuplicate{ ($0.name,$0.age) }

Upvotes: 8

alionthego
alionthego

Reputation: 9743

this is the solution that seems to work for me. maybe not the most elegant but simple enough.

objects is the array of coreData objects. The second array objectsArray is an empty array which is populated in the nested iteration with non age/name duplicate items.

var objects = [Object]()
//initialize the above array objects and fill with core data objects
objects = populateObjectsArrayWithCoreDataObjects()
//create a new empty array called objectsArray
var objectsArray = [Object]()
for index in 0..<self.objects.count {
    let object = objects[index]
    var isDuplicate = false
    for index2 in (index + 1)..<self.objects.count {
        let object2 = objects[index2]
        if object.name == object2.name && object.age == object2.age {
            isDuplicate = true
        }
    }
    if isDuplicate == false {
        objectsArray.append(item)
    }
}
objects = objectsArray

Upvotes: 0

Related Questions