-1

I have a 2d array of floats:

let scores  = Array2D.init<float> width height (fun _ _ -> 0.)

and I would like to convert it to a list of lists where I have a list of rows and each of these row is a list of column values.

for example:

[
  1, 2, 3
  4, 5, 6
  7, 8, 9
]

would become:

[
  [1, 2, 3]
  [4, 5, 6]
  [7, 8, 9]
]

I found this question: F# convert Array2 into a list

but it converts the 2d array into a list, I'm trying to find how to convert it to a list of list. I could iterate through the elements and build the list, but I'm sure there has to be a better way.

Thomas
  • 10,933
  • 14
  • 65
  • 136

2 Answers2

1

This is made quite readable by F#'s slice operators (now in C# 8 too!). To slice a multi-dimensional array you can use .[range,range].

For example, .[*,n] for the n-th column, and .[n,*] for the n-th row.

The final piece we need is the height of the array, which is Dimension 0, given by

array.GetLength 0

Now it becomes trivial to slice up the 2D array, using a list comprehension.

[ 
    let height = arr.GetLength 0
    for row in 0..height-1  do
    yield arr.[row,*] |> List.ofArray
]

For non-zero indexed arrays, as suggested by Henrik Hansen, it is preferable to use:

[ 
    for i in arr.GetLowerBound(0)..arr.GetUpperBound(0) 
        do yield arr.[i,*] |> List.ofArray 
]

Test

let counter = ref 0
//generate the test array
let arr = Array2D.init (3) (3) (fun _ _ -> Interlocked.Increment(counter))

arr |> printfn "%A"  

let lists = 
    [ 
        let height = arr.GetLength 0
        for row in 0..height - 1  do
        yield arr.[row,*] |> List.ofArray
    ]

lists |> printfn "%A"

Output:

[[1; 2; 3]
 [4; 5; 6]
 [7; 8; 9]]

[[1; 2; 3]; [4; 5; 6]; [7; 8; 9]]
Asti
  • 12,447
  • 29
  • 38
1

Another way building on Asti's ideas:

module Array2D =
    let toListOfLists (arr2d: 'a [,]) = [ yield! [arr2d.GetLowerBound(0)..arr2d.GetUpperBound(0)] |> List.map (fun i -> arr2d.[i,*] |> List.ofArray) ]

let base002D = Array2D.init 5 5 (fun x y -> (x, y))
let base152D = Array2D.initBased 1 5 5 5 (fun x y -> (x, y)) 

printfn "%A" (base002D |> Array2D.toListOfLists)
printfn "%A" (base152D |> Array2D.toListOfLists)

It works with different bases.

  • Maybe simpler to just use a single list comprehension? `[ for i in arr2d.GetLowerBound(0)..arr2d.GetUpperBound(0) do yield arr2d.[i,*] |> List.ofArray ]` – Asti Mar 29 '20 at 17:48
  • @Asti: yes, that will do too. The important thing is just, that we'll have to use upper and lower bounds to make it able to handle bases other than zero. –  Mar 29 '20 at 18:58
  • 1
    Absolutely. Although non-zero indexed arrays are relatively rare, and most array code written generally assumes zero-indexing. – Asti Mar 29 '20 at 19:03