21

The filter method is a really powerful tool for filtering by single or multiple conditions, but is there a way to filter by conditions of arrays?

class Car with properties : model, color, engineStatus.

  • cars is an array with few cars

By one condition would look like:

let currModel = `Opel`

let filterdObject = cars.filter { $0.model == currModel }

By two or more conditions would look like:

let currModel = `Opel`
let currColor = `Green`

let filterdObject = cars.filter { $0.model == currModel || $0.color == currColor }

My question is it how could I filter by an array like:

An array has ,e.g., two colors blue and green. I would like to filter cars by these colors. My point is to get a formula for n-conditions.

3
  • 1
    Something like cars.filter {arrayOfColors(contains:$0.color)}? Commented Apr 11, 2017 at 14:11
  • 1
    @Larme would looks like` cars.filter {arrayOfColors.contains($0.color)}` - new syntax Commented Apr 11, 2017 at 14:16
  • I don't use Swift, when I comment on Swift, it's just pointing out what could possibly a way to go, code not assured at all to compile, but understandable. But thanks to correct (in case of someone taking "as such" the indication). Commented Apr 11, 2017 at 14:18

3 Answers 3

60

Forget about the filter for a moment. Think how you would check if a car's color is a value in an array.

let colors = [ "Green", "Blue" ]
// or let colors: Set = [ "Green", "Blue" ]
if colors.contains(someCar.color) {
}

Simple enough. Now use that same simple expression in the filter.

let filterdObject = cars.filter { $0.model == currModel || colors.contains($0.color) }
Sign up to request clarification or add additional context in comments.

2 Comments

I don't get the downvote either, this is a valid solution... Having said that, a membership test against a Set instead of an Array could be more suitable here.
@Alladinian Good suggestion on the use of Set. The OP mentioned having an array.
10

Treat the filter closures like a value type and store them in an array. Use the inner reduce call to create a single boolean value that is true is all of the conditions are met by the current car. If you need a compound test like color == "blue" or "green" then simply add that to your filter closure conditions array.

    struct Car {
      let model: String
      let color: String
    }

    let conditions: [(Car) -> Bool] = [
      {$0.model == "Opel"},
      {$0.color == "Red"},
    ]

    let carLot = [
      Car(model: "Opel", color: "Green"),
      Car(model: "Mustang", color: "Gold"),
      Car(model: "Opel", color: "Red"),
    ]

    let allRedOpels = carLot.filter {
      car in
      conditions.reduce(true) { $0 && $1(car) }
   }

2 Comments

That reduce approach isn't so good because it doesn't perform any short circuiting
Thank you very much for this, it saved my day and a lot of work!
2

To piggy-back off Price Ringo's answer you could use allSatisfy instead of reduce to get a cleaner read of the code:

struct Car {
  let model: String
  let color: String
}

let conditions: [(Car) -> Bool] = [
  {$0.model == "Opel"},
  {$0.color == "Red"},
]

let carLot = [
  Car(model: "Opel", color: "Green"),
  Car(model: "Mustang", color: "Gold"),
  Car(model: "Opel", color: "Red"),
]

let allRedOpels = carLot.filter {
  car in
  conditions.allSatisfy { $0(car) }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.