3
votes

I have an integer array in which I want to send every two elements from it to the constructor of another function.

Something like intArray |> Array.map (fun x, y -> new Point(x, y))

Is this possible? I'm new to F# and functional programming so I'm trying to avoid just looping through every 2 items in the array and adding the point to a list. I hope that's reasonable.

3
Would [1;2;3;4] result in just two points (1,2) and (3,4), or would it be 3 points (1,2) (2,3) and (3,4)? - MarcinJuraszek
[1;2;3;4] would be (1,2) and (3,4). - Shelby115

3 Answers

4
votes

If using F# 4.0, use Gustavo's approach. For F# 3, you can do:

intArray
    |> Seq.pairwise // get sequence of tuples of element (1,2); (2,3); (3,4); (4,5) etc
    |> Seq.mapi (fun i xy -> i, xy) // combine the index with the tuple
    |> Seq.filter (fun (i,_) -> i % 2 = 0) // Filter for only the even indices to get (1,2); (3,4)
    |> Seq.map (fun xy -> Point xy) // make a point from the tuples
    |> Array.ofSeq // convert back to array
4
votes

You can use Array.chunkBySize:

intArray 
    |> Array.chunkBySize 2 
    |> Array.map (function 
        | [|x; y|] -> new Point (x, y) 
        | _ -> failwith "Array length is not even.")
0
votes

An alternative solution to the existing answers, would be to write a custom function, that creates a list/an array of tuples using pattern matching:

let chunkify arr =
    let rec chunkify acc lst =
        if (List.length lst) > 1 then (* proceed if there are at least two elements *)
            match lst with 
            (* save every constructed pair, until the input is not empty *)
            | h1 :: h2 :: tail -> chunkify ([(h1, h2)] @ acc) tail
            | _ -> acc (* else return the list of pairs *)
        else (* return the list of pairs *)
            acc

    chunkify List.empty (List.ofSeq arr) |> List.rev |> Array.ofSeq  

The function can be then used like this:

// helper
let print = (fun (x:'a, y:'a) -> printfn "new Object(%A,%A)" x y)
// ints
[|1;2;3;4;5;6|] 
|> chunkify 
|> Array.iter print
// strings
[|"a";"b";"c";"d";"e"|] 
|> chunkify 
|> Array.map print 
|> ignore

The output is:

new Object(1,2)
new Object(3,4)
new Object(5,6)
new Object("a","b")
new Object("c","d")

This solution/approach uses pattern matching with lists.