Remove duplicate structs in array based on struct property in Swift

妖精的绣舞 提交于 2019-11-28 12:54:18

I really don't want people to just take an answer because it's the only one, that's why I'm showing you how you can use the power of sets. Sets are used wherever it doesn't make sense to have more than one, either it's there or not. Sets provide fast methods for checking whether an element is in the set (contains), removing an element (remove), combining two sets (union) and many more. Often people just want an array because they're familiar with it, but often a set is really what they need. With that said, here is how you can use a set:

struct Model : Hashable {
    var id : String?

    var hashValue: Int {
        return id?.hashValue ?? 0
    }
}

func ==(l: Model, r: Model) -> Bool {
    return l.id == r.id
}

let modelSet : Set = [
    Model(id: "a"),
    Model(id: "hello"),
    Model(id: "a"),
    Model(id: "test")
]

// modelSet contains only the three unique Models

The only requirement for a type in order to be in a set is the Hashable protocol (which extends Equatable). In your case, you can just return the underlying hashValue of the String. If your id is always a number (which it probably is), you should change the type of id to be an Int, because Strings are much less efficient than Ints and it doesn't make sense to use a String.

Also consider storing this property somewhere, so that every time you receive new models, you can just do

modelSet.unionInPlace(newModels)

Use a Set instead of an Array.

If you extend the Array type with this function :

  extension Array
  {
     func uniqueValues<V:Equatable>( value:(Element)->V) -> [Element]
     {
        var result:[Element] = []
        for element in self
        {
           if !result.contains({ value($0) == value(element) })
           { result.append(element) }
        }
        return result
     }
  }

You'll be able to clean up duplicates using :

  var arrayCleaned = arrayOfModel.uniqueValues({$0.id!})

without needing to make the structure Equatable.

Please note that this is not going to be efficient if your array is very large and you might want to consider filtering insertions into your array at the source if possible.

I agree you are better off using a Set. You should be able to initialize the Set using an Array, e.g., var arrayOfModel: Set = [val1, val2, val3]. But because you are using a custom type, you will need to make sure that MyModelStruct conforms to hashable. This link has a good explanation.

But if you want to use an array then you need to change

let areEqual = rhs.id == lhs.id

to

let areEqual = rhs.id == lhs.id && rhs.subId == lhs.subId)

You need to modify your struct to have a subId property (and make the variables Int instead of String.

In answer to your question, yes you do need to iterative over the array.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!