views:

135

answers:

3

i have a List of records

type Item = { Color : string; Size : int}
let itemList = [{Color="Red"; Size=1};
                {Color="Green"; Size=2};
                {Color="Blue"; Size=3};]

I am looking to get turn my list of records into an array of values like [|"Red";"Green";"Blue"|] or [|1;2;3|]

I can sorta get there like this

type ItemType =
| Color of string
| Size of int

type ItemEnum =
| C
| S

let GetProp x y =
match x with
| C -> List.toArray y |> Array.map(fun x -> ItemType.Color(x.Color))
| S -> List.toArray y |> Array.map(fun x -> ItemType.Size(x.Size))

but when I call GetProp S itemList I get back [|Size 1; Size 2; Size 3|]. Useful but not exactly what I'm looking for.

I've tried the following

let GetProp2 x y : 'a[] =
match x with
| Color -> List.toArray y |> Array.map(fun x -> x.Color)
| Size -> List.toArray y |> Array.map(fun x -> x.Size)

but it doesn't like the two different return types.

I'm open to suggestions on different (more functional?) ways of doing this and would appreciate your input.

+1  A: 

Go go gadget Array Comprehensions!

> [| for a in itemList do yield a.Size |];;
val it : int [] = [|1; 2; 3|]
> [| for a in itemList do yield a.Color |];;
val it : string [] = [|"Red"; "Green"; "Blue"|]

You don't need an intermediate ItemType or ItemEnum data structure.

Juliet
That won't help him, as he's trying to write a _single_ function that is returning arrays of _different_ element types, depending on its arguments.
Pavel Minaev
+5  A: 

A custom variant type is indeed the way to go here (and generally wherever you need a type that is "either X or Y"). However, as defined, your function looks like it might return an array in which Color and Size are mixed, but in practice it seems that you only want it to return one or the other. If so, this is best reflected in the types:

type Items =
| Colors of string[]
| Sizes of int[]

let GetProp x ys =
match x with
| C -> Colors [| for y in ys -> y.Color |]
| S -> Sizes [| for y in ys -> y.Size |]

By the way, is there any particular reason why you use array for return type here, rather than the usual lazy sequence (seq)?

Pavel Minaev
the array is the argument type required by the external lib I'm using next. How would I go from List of records to the seq of the particular value?
RobRolls
Any list is already a seq, and so is any array. In .NET terms, seq is `IEnumerable<T>`. If you need to make an array or a list out of any random seq (and possibly a lazy one), then `Seq.toArray` and `Seq.toList` will do the trick.
Pavel Minaev
+4  A: 

You can use active patterns to see your data form few points of view:

let (|AsColor|) (v: Item list) = [| for i in v -> i.Color |];;

let (|AsSize|) (v: Item list) = [| for i in v -> i.Size |];;

match itemList,itemList with
  |AsColor ca, AsSize sa -> printfn "%A; %A" ca sa;;
ssp
I hadn't been exposed to the active pattern yet. Interesting, thanks.
RobRolls