how to calculate the max by column in a list of lists, with F#

Viewed 106

With the following data (random strings):

let data =
    [
        ["shi"; "cjwocij"; "cjwijo"]
        ["abs"; "djw"; "djwjdwojdow"]
        ["djido"; "dkwpkw"; "dpfkpeoep"]
    ]

I would like to calculate the max length by column. I can transform the data into a list of lists of lengths:

data |> List.map (fun r -> r |> List.map (fun x -> x.Length))
index   value
0       [ 3; 7; 6 ]
1       [ 3; 3; 11 ]
2       [ 5; 6; 9 ]

but ultimately I'd like to reduce it to:

[5; 7; 11]

by taking the max of each column

I thought I could do a fold and take the first row as a starting point, but it looks like it'll be quite convoluted.

For context: it's for a function that will take a grid and display it with proper spacing / formatting; all rows must have the same number of elements.

1 Answers

You need List.transpose. This turns rows into columns and vice versa.

data 
|> List.map (fun r -> r |> List.map (fun x -> x.Length)) 
|> List.transpose
|> List.map List.max

val data : string list list =
  [["shi"; "cjwocij"; "cjwijo"]; ["abs"; "djw"; "djwjdwojdow"];
   ["djido"; "dkwpkw"; "dpfkpeoep"]]
val it : int list = [5; 7; 11]

Here is a quick explicit version of transpose that might help (or hinder?) you grokking this:

let transpose lists =
    [0 .. (List.length (List.head lists)) - 1]
    |> List.map (fun col -> List.map (fun row -> List.item col row) lists )

This is the recursive version for completeness but I think the above is clearer if you are new to F#.

let rec transpose = function
    | (_::_)::_ as l -> List.map List.head l :: transpose (List.map List.tail l)
    | _ -> []

Update: I answered this in a comment but for others wondering over the second recursive solution:

The (_::_) ensures that a list is not empty. This is the same as (h::t) but we don't need to use h and t and so put the don't care place holder _ instead.

The second :: makes sure that the list of lists (listoflists) is non-empty or that (h::t)::c = (head of listoflists)::tail of listoflists so h::t is the first row, and c is all the other rows.

(_::_)::_ <=> (headRow0::tailRow0)::tailRows
Related