Removing from a list of a tuples that contains an empty element in the second projection F#

50 Views Asked by At

I need to learn the right way to do pattern matching on Pair types:

let pairToBeFiltered = Ok ([(1,[]);(2,[3;4]);(5,[6;7;8]);(9,[]);(10,[])])

let filterEmpty (pair: int * int list) =
    match pair with
    | (x,y) when y <> [] -> (x,y) //This gives error because of incomplete pattern matching!

let filtering = List.map(filterEmpty) pairToBeFiltered

Desired output:

Ok([(2,[3;4]);(5,[6;7;8])])
2

There are 2 best solutions below

0
Brian Berns On

This should do it:

let pairsToBeFiltered = Ok ([(1,[]);(2,[3;4]);(5,[6;7;8]);(9,[]);(10,[])])

let filterEmpty pairs =
    List.where (fun (_, y) -> y <> []) pairs   // pattern match on pair occurs here

let filtering : Result<_, string> =
    pairsToBeFiltered
        |> Result.map filterEmpty

printfn "%A" filtering   // Ok [(2, [3; 4]); (5, [6; 7; 8])]

There are a number of issues here:

  • For clarity, I modified filterEmpty so it processes the entire list, rather than a single pair. This is where we apply the filtering function, List.where, using pattern matching. (In your code, note that List.map with a match expression doesn't filter anything.)
  • Since your list is wrapped in a Result, you need to unwrap it via Result.map in order to process it. (Since you didn't specify a 'TError type, I assumed string to pacify the compiler.)
0
Roland Andrag On

Three more versions:

(* using match statement *)
module Version1 =
   let pairsToBeFiltered : Result<_, string> =
      Ok [(1,[]);(2,[3;4]);(5,[6;7;8]);(9,[]);(10,[])]

   let myWhere (pair : int * List<int>) =
      match pair with
      | _, [] -> false
      | _, _ -> true

   let myFilter l0 = l0 |> Result.map (List.filter myWhere)

   let result = pairsToBeFiltered |> myFilter


(* using lambda functions and List.isEmpty *)
module Version2 =
   let pairsToBeFiltered : Result<_, string> =
      Ok [(1,[]);(2,[3;4]);(5,[6;7;8]);(9,[]);(10,[])]

   let myFilter l0 =
      l0 
      |> Result.map (fun l1 -> 
            l1 |> List.filter (fun (_, l2) -> 
               l2 |> List.isEmpty |> not))

   let result = pairsToBeFiltered |> myFilter


(* shortening Version2 (point free style - take care, can be confusing)  *)
module Version3 =
   let pairsToBeFiltered : Result<_, string> =
      Ok [(1,[]);(2,[3;4]);(5,[6;7;8]);(9,[]);(10,[])]

   let myFilter = Result.map (List.filter (snd >> List.isEmpty >> not))

   let result = pairsToBeFiltered |> myFilter

Related Questions in F#