Jamie
Jamie

Reputation: 63

Swift 5, Array of custom object, lowest value. Not equal to other values in same array

How do you determine if this array has only a single lowest value?

let scoresExampleOne = [2, 2, 3, 4] // return false

let scoresExampleTwo = [2, 3, 3, 5] // return true 

"scoreValues" are embedded in a custom "Player" object.

I just tried to simplify it for the sake of this question.

Upvotes: 0

Views: 176

Answers (3)

Leo Dabus
Leo Dabus

Reputation: 236420

All you need is to iterate your collection and keep track of the minimum value and if it repeats or not:

extension Collection {
    func minElement<T: Comparable>(_ predicate: (Element) -> T) -> (element: Element, single: Bool)? {
        guard var minElement = first else { return nil }
        var min = predicate(minElement)
        var single = true
        for element in dropFirst() {
            let value = predicate(element)
            if value > min { continue }
            if value < min {
                minElement = element
                min = value
                single = true
            } else {
                single = false
            }
        }
        return (minElement, single)
    }
    func min<T: Comparable>(_ predicate: (Element) -> T) -> (min: T, single: Bool)? {
        guard let (element, single) = minElement(predicate) else { return nil }
        return (predicate(element), single)
    }
}

Playground testing:

struct Player {
    let score: Int
}

let players1: [Player] = [.init(score: 2),
                          .init(score: 2),
                          .init(score: 3),
                          .init(score: 4)]
let players2: [Player] = [.init(score: 2),
                          .init(score: 3),
                          .init(score: 3),
                          .init(score: 5)]

let scoresExampleOne = players1.min(\.score)          // (min 2, single false)
let scoresExampleTwo = players2.min(\.score)          // (min 2, single true)
let scoresExampleThree = players1.minElement(\.score) // ({score 2}, single false)
let scoresExampleFour = players2.minElement(\.score)  // ({score 2}, single true)

Upvotes: 3

user652038
user652038

Reputation:

Leo's answer is good, but it's a special case of this extremum method.

public extension Sequence {
  /// The first element of the sequence.
  /// - Note: `nil` if the sequence is empty.
  var first: Element? {
    var iterator = makeIterator()
    return iterator.next()
  }

  /// - Parameters:
  ///   - comparable: The property to compare.
  ///   - areSorted: Whether the elements are in order, approaching the extremum.
  func extremum<Comparable: Swift.Comparable>(
    comparing comparable: (Element) throws -> Comparable,
    areSorted: (Comparable, Comparable) throws -> Bool
  ) rethrows -> Extremum<Element>? {
    try first.map { first in
      try dropFirst().reduce(into: .init(value: first, count: 1)) {
        let comparables = (try comparable($0.value), try comparable($1))

        if try areSorted(comparables.0, comparables.1) {
          $0 = .init(value: $1, count: 1)
        } else if (comparables.0 == comparables.1) {
          $0.count += 1
        }
      }
    }
  }

  /// - throws: `Extremum<Element>.UniqueError`
  func uniqueMin<Comparable: Swift.Comparable>(
    comparing comparable: (Element) throws -> Comparable
  ) throws -> Extremum<Element> {
    typealias Error = Extremum<Element>.UniqueError

    guard let extremum = try extremum(comparing: comparable, areSorted: >)
    else { throw Error.emptySequence }

    guard extremum.count == 1
    else { throw Error.notUnique(extremum) }

    return extremum
  }
}
public struct Extremum<Value> {
  enum UniqueError: Swift.Error {
    case emptySequence
    case notUnique(Extremum)
  }

  var value: Value
  var count: Int
}

Upvotes: 0

Sulthan
Sulthan

Reputation: 130122

func isSingleLowestValue(scores: [Int]) -> Bool {
  guard let min = scores.min() else { return false }

  let minCount = scores.lazy.filter { $0 == min }.count
  return minCount == 1
}

Upvotes: 1

Related Questions