It can certainly be written with filter or map or reduce, but that makes it pretty hard to read.
It might be hard to read if you build it on those primitives, but in this case there’s a
max(_:)
method that does exactly this job. It also saves you from one gotcha, namely that it returns an optional value, making it clear that there may be no maximum. The code you posted assumes that 0 is a valid sentinel for that, but there’s no guarantee that this is the case.
So, if you want to assume that every dictionary has a valid
totalRating
property, you could write this:
let dictArray = [
["totalRating":100,"uid":"Id-1"],
["totalRating":200,"uid":"Id-2"],
["totalRating":20,"uid":"Id-4"]
]
let d = dictArray.max { l, r in
return (l["totalRating"] as! Int) > (r["totalRating"] as! Int)
}
where
d
is an optional dictionary. If you don’t want to assume that, then you could write this:
let d = dictArray.max { l, r in
let lRating = l["totalRating"] as? Int
let rRating = r["totalRating"] as? Int
switch (lRating, rRating) {
case (nil, nil): return false
case (nil, _): return true
case (_, nil): return false
case (let l?, let r?): return l < r
}
}
although I prefer this:
let d = dictArray.compactMap { element in
(element["totalRating"] as? Int).flatMap { (value: $0, element: element) }
}.max { l, r in
l.value < r.value
}.flatMap { $0.element }
Personally, I’d simplify the whole problem by getting rid of the
[String: Any]
dictionary. If it’s a precondition of this code that every item has a
totalRating
value, I’d check that when I import the data into my app and then represent each item as a
Rating
type:
struct Rating {
var totalRating: Int
var uid: String // use `String?` if `uid` is not mandatory
}
That allows for a radical simplification of the code:
let dictArray = [
Rating(totalRating: 100, uid: "Id-1"),
Rating(totalRating: 200, uid: "Id-2"),
Rating(totalRating: 20, uid: "Id-4"),
]
let d = dictArray.max { l, r in
l.totalRating < r.totalRating
}
And if you want all items with that total rating, that’s just a
flatMap(_:)
away:
let dictArray = [
Rating(totalRating: 100, uid: "Id-1"),
Rating(totalRating: 200, uid: "Id-2"),
Rating(totalRating: 20, uid: "Id-4"),
Rating(totalRating: 200, uid: "Id-5"),
]
let all = dictArray.max { l, r in
l.totalRating < r.totalRating
}.flatMap { d in
dictArray.filter { $0.totalRating == d.totalRating }
} ?? []
One final advantage of using a real type here is that, if the data is coming from a web service, you could make the
Rating
type
Codable
and thus easily build it from the JSON returned by that web service.
Share and Enjoy
—
Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware
let myEmail = "eskimo" + "1" + "@apple.com"