Remove duplicates from array of dictionaries, Swif

2020-07-28 11:30发布

问题:

Problem

I have an array of dictionaries as follows:

var arrayOfDicts = [
    ["Id":"01", "Name":"Alice", "Age":"15"]
    ["Id":"02", "Name":"Bob", "Age":"53"]
    ["Id":"03", "Name":"Cathy", "Age":"12"]
    ["Id":"04", "Name":"Bob", "Age":"83"]
    ["Id":"05", "Name":"Denise", "Age":"88"]
    ["Id":"06", "Name":"Alice", "Age":"44"]
]

I need to remove all dictionaries where there is a duplicate name. For instance, I need an output of:

var arrayOfDicts = [
    ["Id":"01", "Name":"Alice", "Age":"15"]
    ["Id":"02", "Name":"Bob", "Age":"53"]
    ["Id":"03", "Name":"Cathy", "Age":"12"]
    ["Id":"05", "Name":"Denise", "Age":"88"]
]

Order does not need to be preserved.

Attempted Solution

for i in 0..<arrayOfDicts.count
{
    let name1:String = arrayOfDicts[i]["Name"]

    for j in 0..<arrayOfDicts.count
    {
        let name2:String = arrayOfDicts[j]["Name"]

        if (i != j) && (name1 == name2)
        {
            arrayOfDicts.remove(j)
        }
    }
} 

This crashes though, I believe since I am modifying the size of arrayOfDicts, so eventually it j is larger than the size of the array.

If someone could help me out, that would be much appreciated.

回答1:

I definitely recommend having a new copy rather than modifying the initial array. I also create storage for names already used, so you should only need to loop once.

func noDuplicates(_ arrayOfDicts: [[String: String]]) -> [[String: String]] {
    var noDuplicates = [[String: String]]()
    var usedNames = [String]()
    for dict in arrayOfDicts {
        if let name = dict["name"], !usedNames.contains(name) {
            noDuplicates.append(dict)
            usedNames.append(name)
        }
    }
    return noDuplicates
}


回答2:

You can use a set to control which dictionaries to add to the resulting array. The approach it is very similar to the one used in these answer and this

let array: [[String : Any]] = [["Id":"01", "Name":"Alice", "Age":"15"],
                                ["Id":"02", "Name":"Bob", "Age":"53"],
                                ["Id":"03", "Name":"Cathy", "Age":"12"],
                                ["Id":"04", "Name":"Bob", "Age":"83"],
                                ["Id":"05", "Name":"Denise", "Age":"88"],
                                ["Id":"06", "Name":"Alice", "Age":"44"]]

var set = Set<String>()
let arraySet: [[String: Any]] = array.compactMap {
    guard let name = $0["Name"] as? String else { return nil }
    return set.insert(name).inserted ? $0 : nil
}

arraySet   // [["Name": "Alice", "Age": "15", "Id": "01"], ["Name": "Bob", "Age": "53", "Id": "02"], ["Name": "Cathy", "Age": "12", "Id": "03"], ["Name": "Denise", "Age": "88", "Id": "05"]]


回答3:

Please check this answer:

var arrayOfDicts = [
    ["Id":"01", "Name":"Alice", "Age":"15"],
    ["Id":"02", "Name":"Bob", "Age":"53"],
    ["Id":"03", "Name":"Cathy", "Age":"12"],
    ["Id":"04", "Name":"Bob", "Age":"83"],
    ["Id":"05", "Name":"Denise", "Age":"88"],
    ["Id":"06", "Name":"Alice", "Age":"44"]
]

var answerArray = [[String:String]]()

for i in 0..<arrayOfDicts.count
{
    let name1 = arrayOfDicts[i]["Name"]
    if(i == 0){
        answerArray.append(arrayOfDicts[i])
    }else{
        var doesExist = false
        for j in 0..<answerArray.count
        {
            let name2:String = answerArray[j]["Name"]!
            if name1 == name2 {
                doesExist = true
            }
        }
        if(!doesExist){
            answerArray.append(arrayOfDicts[i])
        }
    }
}


回答4:

Several good answers already, but it was a fun exercise, so here's my solution. I'm assuming you don't care which of the duplicate entries are kept (this will keep the last one of the dupes).

func noDuplicates(arrayOfDicts: [[String:String]]) -> [[String:String]]
{
    var noDuplicates: [String:[String:String]] = [:]
    for dict in arrayOfDicts
    {
        if let name = dict["name"]
        {
            noDuplicates[name] = dict
        }
    }

    // Returns just the values of the dictionary
    return Array(noDuplicates.values.map{ $0 })
}


回答5:

let uniqueArray = Array(Set(yourArrayWithDuplicates))

That should do the trick.

If you want to use just the name for uniqueness then create these as structs.

You shouldn't be doing anything with dictionaries. Much easier to work with data that makes sense.



回答6:

Try this:

var uniqueNames = [String: [String:String] ]()

for air in arrayOfDicts {
    if (uniqueNames[arr["Name"]!] == nil) {
        uniqueNames[arr["Name"]!] = arr
    }
}

result = Array(uniqueNames.values)


回答7:

If you don't mind using an additional list:

var uniqueArray = [[String: String]]()
for item in arrayOfDicts {
    let exists =  uniqueArray.contains{ element in
        return element["Name"]! == item["Name"]!
    }
    if !exists {
      uniqueArray.append(item)
    }
}


标签: ios swift xcode