1

I have two arrays

let badContents = ["b1", "b2"]
let things: [Thing] = ...

where a Thing has its own contents, like this

print(things[0].contents)
// ["g1", "b1", "b2"]

I wanted to do something like the below, where I would get an array of type Thing whose elements had contents that did not overlap with another array, badContents

func filteredThings() -> [Thing] {
    return things.filter({ (thing) -> Bool in {
        return // thing.contents and badContents do not share any elements
    }()
    })
}

Thus, I would get a result like this

let things = [Thing(name: "1", contents: ["g1", "b2"), Thing(name: "2", contents: ["g1", "g2"])]

let goodThings = filteredThings() // removes Thing named "1" because its contents contain "b2"

for goodThing in goodThings {
    print(goodThing.name)
    // "2"
}
rdk
  • 439
  • 3
  • 14
  • 2
    [How to get list of common elements of 2 arrays in Swift?](https://stackoverflow.com/q/32439289) – jscs Dec 09 '17 at 20:33
  • @JoshCaswell Thanks but thats not actually what I want to do. The main difference is that I am trying to get a result of type `[Thing]` who has a _member property_ that is comparable to the other array. The linked question would work maybe if my `badContents` was of type `[Thing]` – rdk Dec 09 '17 at 20:43
  • 4
    @rdk: In your filter method you want to check if *"thing.contents and badContents do not share any elements"* – in other words, if these 2 arrays have no common elements. Therefore the above link should help to find a solution. – Martin R Dec 09 '17 at 20:57
  • @MartinR Thanks! I now see how to use that in my solution! – rdk Dec 09 '17 at 21:32
  • Please post your solution as an answer, and mark it as accepted. P.S. that `arrayOfCommonElements` is a really crappy O(N^2) solution – Alexander Dec 09 '17 at 23:31

2 Answers2

1

It probably doesn't make much difference for performance (unless badThings is large, or contents is both large and "bad things" are common), but I'd probably still do it this way instead, which doesn't require any new extensions:

let badContents = Set(["b1", "b2"])

func filteredThings() -> [Thing] {
    return things.filter { $0.contents.first(where: { badContents.contains($0) }) == nil }
}

Even if you keep your approach, I'd stop searching when you found a collision. Finding all the collisions and then checking .count == 0 is just kind of wasteful without being particularly easier to read.

Alternately, while a little less efficient in time and space, the following IMO is extremely explicit:

let badContents = Set(["b1", "b2"])

func filteredThings2() -> [Thing] {
    return things.filter { Set($0.contents).intersection(badContents).isEmpty }
}
Rob Napier
  • 286,113
  • 34
  • 456
  • 610
0
func filteredThings() -> [Thing] {
    return things.filter({ (thing) -> Bool in {
        return arrayOfCommonElements(lhs: thing.contents, rhs: badContents).count == 0
    }()
    })
}
func arrayOfCommonElements <T, U> (lhs: T, rhs: U) -> [T.Iterator.Element] where T: Sequence, U: Sequence, T.Iterator.Element: Equatable, T.Iterator.Element == U.Iterator.Element {
    var returnArray:[T.Iterator.Element] = []
    for lhsItem in lhs {
        for rhsItem in rhs {
            if lhsItem == rhsItem {
                returnArray.append(lhsItem)
            }
        }
    }
    return returnArray
}
rdk
  • 439
  • 3
  • 14