Search code examples
f#seq

Best way to do tryMax and tryMin in F#?


Suppose I have a seq and I want to return the largest if there are any elements or None otherwise. F# does not appear to have this built-in.

Here is my attempt:

let tryMax xs = 
  if Seq.isEmpty xs
  then 
    None
  else 
    Seq.max xs |> Some

let tryMin xs = 
  if Seq.isEmpty xs
  then 
    None
  else 
    Seq.min xs |> Some
  • Are there any problems with this approach?
  • Is there a built-in solution for this?

Solution

  • FWIW, here's tryMinBy as well:

    let tryMinBy projection (items : seq<_>) =
        use e = items.GetEnumerator()
        if e.MoveNext() then
            let mutable minItem = e.Current
            let mutable minValue = projection minItem
            while e.MoveNext() do
                let value = projection e.Current
                if value < minValue then
                    minItem <- e.Current
                    minValue <- value
            Some minItem
        else
            None
    

    The full suite:

    module Seq
    
    let tryMinBy projection (items : seq<_>) =
      use e = items.GetEnumerator ()
    
      if e.MoveNext ()
      then
        let mutable minItem = e.Current
        let mutable minValue = projection minItem
    
        while e.MoveNext () do
          let value = projection e.Current
    
          if value < minValue
          then
            minItem <- e.Current
            minValue <- value
    
        Some minItem
      else
        None
    
    let tryMaxBy projection (items : seq<_>) =
      use e = items.GetEnumerator ()
    
      if e.MoveNext ()
      then
        let mutable maxItem = e.Current
        let mutable maxValue = projection maxItem
    
        while e.MoveNext () do
          let value = projection e.Current
    
          if value > maxValue
          then
            maxItem <- e.Current
            maxValue <- value
    
        Some maxItem
      else
        None
    
    let tryMin (items : seq<_>) =
      use e = items.GetEnumerator ()
    
      if e.MoveNext ()
      then
        let mutable minItem = e.Current
    
        while e.MoveNext () do
          if e.Current < minItem
          then
            minItem <- e.Current
    
        Some minItem
      else
        None
    
    let tryMax (items : seq<_>) =
      use e = items.GetEnumerator ()
    
      if e.MoveNext ()
      then
        let mutable maxItem = e.Current
    
        while e.MoveNext () do
          if e.Current > maxItem
          then
            maxItem <- e.Current
    
        Some maxItem
      else
        None