tombulbu
tombulbu

Reputation: 21

Finding the largest number in a [String: [Int]] Swift dictionary

I am new in Swift. I have an error with this code and I can't find any answer on this site. I print largest number but I want to print largest number's kind.

let interestingNumbers = [
    "Prime": [2,3,5,7,11,13],
    "Fibonacci": [1,1,2,3,5,8,13],
    "Square": [1,4,9,16,25,36]
]

var largestNumber = 0

for (kind, numbers) in interestingNumbers {
    for x in numbers {
        for y in kind {
            if x > largestNumber {
                largestNumber = x
            }
        }
    }
}

print("the largest number is = \(largestNumber)")

Upvotes: 2

Views: 1073

Answers (2)

Rob Napier
Rob Napier

Reputation: 299455

There is nothing wrong with Paulo's approach (with some minor corrections; see comments there), but this is a reasonable problem to explore more functional approaches that don't require looping and mutation.

For example, we can just flatten each kind to its maximum element (or Int.min if it's empty), then take the kind with the highest max:

interestingNumbers
    .map { (kind: $0, maxValue: $1.max() ?? .min) } // Map each kind to its max value
    .max { $0.maxValue < $1.maxValue }?             // Find the kind with the max value
    .kind                                           // Return the kind

This does create a slight edge condition that I don't love. If you evaluate the following:

let interestingNumbers = [
    "ImaginaryReals": [],
    "Smallest": [Int.min],
]

It's not well defined here which will be returned. Clearly the correct answer is "Smallest," but it's kind of order-dependent. With a little more thought (and code) we can fix this. The problem is that we are taking a little shortcut by treating an empty list as having an Int.min maximum (this also prevents our system from working for things like Float, so that's sad). So let's fix that. Let's be precise. The max of an empty list is nil. We want to drop those elements.

We can use a modified version of mapValues (which is coming in Swift 4 I believe). We'll make flatMapValues:

extension Dictionary {
    func flatMapValues<T>(_ transform: (Value) throws -> T?) rethrows -> [Key: T] {
        var result: [Key: T] = [:]

        for (key, value) in self {
            if let newValue = try transform(value) {
                result[key] = newValue
            }
        }
        return result
    }
}

And with that, we can be totally precise, even with empty lists in the mix:

interestingNumbers
    .flatMapValues { $0.max() }
    .max { $0.1 < $1.1 }?
    .key

Again, nothing wrong with Paulo's approach if you find it clear, but there are other ways of thinking about the problem.

BTW, the equivalent version that iterates would look like this:

var largestNumber: Int? = nil
var largestNumberKind: String? = nil

for (kind, numbers) in interestingNumbers {
    for x in numbers {
        if largestNumber == nil || largestNumber! < x {
            largestNumber = x
            largestNumberKind = kind
        }
    }
}

Upvotes: 1

Paulo Mattos
Paulo Mattos

Reputation: 19339

Try this instead:

var largestNumber = 0
var largestNumberKind: String!

for (kind, numbers) in interestingNumbers {
    for x in numbers {
        if x > largestNumber {
            largestNumber = x
            largertNumberKind = kind
        }
    }
}

print("the largest number is = \(largestNumber)")
print("the largest number kind is = \(largestNumberKind)")

Regarding your original code:

  • you were only keeping track of the largest number, losing the kind info you wanted. The largestNumberKind variable I added does just that.
  • looping over the kind: String didn't make any sense (the for y in kind line). Your outside loop already iterates a key at a time, so such inner loop is pointless.

Upvotes: 2

Related Questions