Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to do Seq.unzip

Tags:

f#

I have a foo: seq<int*int>

I want to split the tupled items and then store the results to two variables, each a seq<int>

I was wondering if there was a prettier way to do this, e.g.

let item1, item2 = foo |> ?????

My current solution:

let item1 = foo |> Seq.map(fun (f1,_) -> f1)
let item2 = foo |> Seq.map(fun (_,f2) -> f2)
like image 989
reborn programmer Avatar asked May 04 '16 18:05

reborn programmer


4 Answers

Sadly, there is no drop-in Seq.unzip function included in the language, although equivalents for Lists (List.unzip) and Arrays (Array.unzip) do exist.

There are a few ways of defining such a function, one method is this:

let unzip sequence =
    let (lstA, lstB) = 
        Seq.foldBack (fun (a,b) (accA, accB) -> 
            a::accA, b::accB) sequence ([],[])
    (Seq.ofList lstA, Seq.ofList lstB)

Alternatively, if you don't care about switching back and forth between list, you can just do:

let item1, item2 = 
    let it1, it2 = 
        foo 
        |> List.ofSeq 
        |> List.unzip 
    (Seq.ofList it1, Seq.ofList it2)
like image 99
TheInnerLight Avatar answered Sep 28 '22 01:09

TheInnerLight


What you want to get are two different sequences, so you won't find a much prettier way to do this. What you have is almost sufficient, but you can make it a bit shorter by using fst and snd for teasing first and second items of the tuples respectively, and writing both expression on the same line:

let items1, items2 = foo |> Seq.map fst, foo |> Seq.map snd
like image 20
Fyodor Soikin Avatar answered Sep 28 '22 02:09

Fyodor Soikin


For lists and arrays, these functions are built-in:

> [(1, "foo"); (2, "bar"); (3, "baz")] |> List.unzip;;
val it : int list * string list = ([1; 2; 3], ["foo"; "bar"; "baz"])
> [|(1, "foo"); (2, "bar"); (3, "baz")|] |> Array.unzip;;
val it : int [] * string [] = ([|1; 2; 3|], [|"foo"; "bar"; "baz"|])

It doesn't exist for Seq, though.

like image 45
Mark Seemann Avatar answered Sep 28 '22 03:09

Mark Seemann


To add on TheInnerLight's answer, the official documentation tells us that :

Seq.zip and Seq.zip3 take two or three sequences and produce a sequence of tuples. These functions are like the corresponding functions available for lists. There is no corresponding functionality to separate one sequence into two or more sequences. If you need this functionality for a sequence, convert the sequence to a list and use List.unzip.

My spontaneous solution (similar to the OP's approach) would have been to write :

let unzip sequence = 
    let source = Seq.cache sequence
    Seq.map fst source, Seq.map snd source

(I tested it in FSI for 1000000 elements and it is also a bit slower than the List.unzip approach on my computeur)

like image 29
Nestor Demeure Avatar answered Sep 28 '22 01:09

Nestor Demeure