Remove duplicate structs in array based on struct

2019-01-20 04:50发布

I have made a simple struct and implemented the Equatable protocol :

extension MyModelStruct: Equatable {}

func ==(lhs: NModelMatch, rhs: NModelMatch) -> Bool {
    let areEqual = lhs.id == rhs.id
    return areEqual
}

public struct MyModelStruct {

    var id : String?
    var staticId : String?

    init(fromDictionary dictionary: NSDictionary){
        id = dictionary["id"] as? String
        ...
}

Then in my project i get an array of [MyModelStruct], what i what to do is to remove all the MyModelStruct that have the same id

let val1 = MyModelStruct(id:9, subId:1)
let val2 = MyModelStruct(id:10, subId:1)
let val3 = MyModelStruct(id:9, subId:10)

var arrayOfModel = [val1,val2,val3]; // or set but i do not know how to use a set
var arrayCleaned = cleanFunction[M2,M3] 

How can i make the cleanFunction ?

Can someone help please. Thanks for all. Xcode : Version 7.3.1

4条回答
来,给爷笑一个
2楼-- · 2019-01-20 05:28

Use a Set instead of an Array.

查看更多
放荡不羁爱自由
3楼-- · 2019-01-20 05:29

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.

查看更多
迷人小祖宗
4楼-- · 2019-01-20 05:39

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.

查看更多
小情绪 Triste *
5楼-- · 2019-01-20 05:54

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)
查看更多
登录 后发表回答