Markus Johansson Markus Johansson - 4 months ago 22
Swift Question

Delete array of structs from an array of structs Swift

I have an array of structs that looks like this:

struct minStruct {

var namn:String?
var imag:UIImage?
var rea:String?
var comp:String?


}

var structArr = [minStruct]()


If I would like to remove a specific struct from that array, I could simply do this:

var oneStruct = minStruct(namn: "Name", imag: UIImage(named: "Image"), rea: "Something", comp: "Something")

if structArr.filter({$0.namn == oneStruct!.namn}).count > 0 {

structArr = structArr.filter({$0.namn != oneStruct!.namn})


}

However, what I would like to do is to remove an array of structs from structArr. Something like this:

structArr = [minStruct(namn: "Name", imag: UIImage(named: "Image"), rea: "Something", comp: "Something"), minStruct(namn: "secondName", imag: UIImage(named: "secondImage"), rea: "Something2", comp: "Something2"), minStruct(namn: "thirdName", imag: UIImage(named: "thirdImage"), rea: "Something3", comp: "Something3")]

var arrToDelete = [minStruct(namn: "Name", imag: UIImage(named: "Image"), rea: "Something", comp: "Something"), minStruct(namn: "secondName", imag: UIImage(named: "secondImage"), rea: "Something2", comp: "Something2")]


So what I want to do is to delete all the items that are inside of arrToDelete from arrStruct. In this example, arrToDelete contains two of the three structs that structArr contains. I want to delete these two structs and keep the one struct that arrToDelete did not contain. I hope that I was clear enough!

Answer

Hashable

So we have struct. First of all let's make it Hashable

struct Element: Hashable {
    var name: String?
    var image: UIImage?
    var rea: String?
    var comp: String?
    var hashValue: Int { return name?.hashValue ?? image?.hashValue ?? rea.hashValue ?? comp.hashValue ?? 0 }
}

func ==(left:Element, right:Element) -> Bool {
    return left.name == right.name && left.image == right.image && left.rea == right.rea && left.comp == right.comp
}

Data

Next we have these 2 arrays

let elms : [Element] = [
    Element(name:"a", image:nil, rea:nil, comp:nil),
    Element(name:"b", image:nil, rea:nil, comp:nil),
    Element(name:"c", image:nil, rea:nil, comp:nil)
]

let shouldBeRemoved: [Element] = [
    Element(name:"b", image:nil, rea:nil, comp:nil),
    Element(name:"c", image:nil, rea:nil, comp:nil)
]

Solution #1

If you DO NOT care about the original sorting you can use

let filtered = Array(Set(elms).subtract(shouldBeRemoved))

Solution #2

If you DO care about the original sorting

let shouldBeRemovedSet = Set(shouldBeRemoved)
let filtered = elms.filter { !shouldBeRemovedSet.contains($0) }

Why didn't I just write this?

let filtered = elms.filter { !shouldBeRemoved.contains($0) }

The line above is a correct solution. However invoking contains on Array is generally slower (usually n/2 checks need to be performed) than invoking it on a Set (usually a single check).