0

I just learned about the wonderful world of map, flatMap and reduce in swift and I already use it where ever it makes sense and helps improve my code.

Now I encountered a very special problem and I wonder if there is a solution to this using map, flatMap and/or reduce.

In my model class, I have a optional array of other models. These models have a optional Bool property. I now want to know if the whole array of models contains at least one with a true property. This is what I'm currently doing:

class ModelA{
  var bModels: [ModelB]?
}
class ModelB{
  var aBool: Bool?
}

func hasATrue(aModel: ModelA) {
    guard let bModels = aModel.bModels else { return false }
    for bModel in bModels {
        if bModel.aBool == true {
            return true
        }
    }
    return false
}
xxtesaxx
  • 6,175
  • 2
  • 31
  • 50
  • 2
    You're looking for `contains(where:)` – compare http://stackoverflow.com/q/29679486/2976878 – Hamish May 11 '17 at 14:37
  • In your case `return aModel.bModels?.contains(where: { $0.aBool == true }) ?? false` – Hamish May 11 '17 at 14:40
  • 1
    Or `return aModel.bModels?.contains(where: { $0.aBool == true }) == true` – more symmetry :) – Martin R May 11 '17 at 14:40
  • You could also use reduce: `return aModel.bModels?.reduce(false) { $0 || $1.aBool ?? false } == true` – paulvs May 11 '17 at 15:12
  • 2
    @paulvs: I would not recommend that. `reduce` does not short-circuit as `contains` does. – Martin R May 11 '17 at 15:34
  • True @MartinR, thanks for the tip! – paulvs May 11 '17 at 15:49
  • Please consider also to learn something *about the wonderful world of* non-optional types ;-) For example in most cases an optional `Bool` is questionable. – vadian May 11 '17 at 16:50
  • Woa thank you, you guys are awesome. :) @vadian, since the model is loaded from a web service, the json may or may not contain the field. I think Bool? reflects that very well and allows me to basically have 3 states (true, false, nil) which I indeed treat differently. Fo example if I have a label, true means text a, false means text b and nil means hide the label – xxtesaxx May 11 '17 at 17:20

2 Answers2

0

You can do this all in a single expression with optional chaining, Array.contains(where:) and nil coalescence.

func hasATrue(aModel: ModelA) {
    return aModel.bModels?.contains(where: { $0.aBool == true }) ?? false
}
Alexander
  • 59,041
  • 12
  • 98
  • 151
0

As per the comments on my question, this is what will do the trick:

return aModel.bModels?.contains(where: { $0.aBool == true }) ?? false

or alternatively

return aModel.bModels?.contains(where: { $0.aBool == true }) == true

Is one better than the other? I don't know. Maybe a Swift expert with deep knowledge about how the language is implemented can tell which one is faster/more efficient but for me, that method is called rarely and processing time is therefore not an issue but that is another topic.

Thanks to all the commenters. I was so fascinated by map, flatMap and reduce that I did not see the obvious (nor did I know that you can pass a closure to a contains function in swift). From day to day I like the language more and more.

I'm almost crying every time I have to write the Java code for Android

xxtesaxx
  • 6,175
  • 2
  • 31
  • 50
  • If your question is answered, please accept one of the answers. – Alexander May 11 '17 at 17:45
  • Or `return aModel.bModels?.contains(where: { $0.aBool ?? false }) ?? false` :) – Martin R May 11 '17 at 17:46
  • @Alexander, can you add all 3 variation in your answer? Then I'll accept yours and delete my own one :) – xxtesaxx May 11 '17 at 17:52
  • @xxtesaxx Those are not my answers, I won't steal credit by putting them in my answer. Their respective owners can post them, if they're interested. – Alexander May 11 '17 at 17:53
  • From a performance perspective, `$0.aBool == true` implicitly wraps the `rhs` (`true`) into an optional boolean, and then the implementation of `==` unwraps both the left and right side. The compiler probably optimizes away most of it, but there are a lot more steps introduced than just `$0.aBool ?? false` – Alexander May 11 '17 at 17:54
  • Very noble of you. I'll accept your answer as correct but I'll leave this discussion here. Thanks again to everybody who participated in educating me today. :) – xxtesaxx May 11 '17 at 18:15