I have a function witch is written in an imperative style and cant get my head around on how to convert it to a more robust functional approach.
The function takes a seq of strings and returns a seq of tuples where each tuple consists of the 2,7,12,.. and 5,10,15,.. item from the input.
Example:
Input = { "Lorem", "ipsum", "dolor", "set", "amet", "consectetuer", "adipiscing", "elit", "Aenean", "commodo", "ligula", "eget", "dolor", "Aenean", "massa" }
Ouput = { ("ipsum", "amet"), ("adipiscing", "commodo"), ("eget", "massa") }
let convert (input : seq<string>) : seq<(string * string)> =
let enum = input.GetEnumerator()
let index = ref 0
let first = ref ""
let second = ref ""
seq {
while enum.MoveNext() do
let modIndex = !index % 5
index := !index + 1
if (modIndex % 2 = 0 && !first = "") then first := enum.Current
if (modIndex % 5 = 0 && !second = "") then second := enum.Current
if modIndex = 0 then
let result = (!first, !second)
first := ""
second := ""
yield result
}
Any help or tip for a starting point is appreciated.
I do not completely understand the behaviour you want - what is the algorithm for generating indices that you want to pair? Anyway, one nice functional solution is to take the elements you want to pair separately and then combine them using Seq.zip
.
You can use Seq.mapi
to add indices to the values and then use Seq.choose
to get the values with the right index (and skip all other values). For hardcoded indices, you can write something like:
let indexed = input |> Seq.mapi (fun i s -> i, s)
Seq.zip
(indexed |> Seq.choose (fun (i, v) -> if i=1 || i=6 || i=11 then Some v else None))
(indexed |> Seq.choose (fun (i, v) -> if i=4 || i=9 || i=14 then Some v else None))
I used your numbers -1 because the indices are from 0 - so the above gives you the results you wanted. The second series looks like multiples of 5, so perhaps you wanted i%5 = 4
to generate second elements:
let indexed = input |> Seq.mapi (fun i s -> i, s)
Seq.zip
(indexed |> Seq.choose (fun (i, v) -> if i=1 || i=6 || i=11 then Some v else None))
(indexed |> Seq.choose (fun (i, v) -> if i%5 = 4 then Some v else None))
I still don't see the general mechanism for generating the first elements though!
EDIT One more idea - is the first sequence generated by i*5 + 2
and the second by i*5
? In that case, your example is wrong, but you could write it like this:
let indexed = input |> Seq.mapi (fun i s -> i, s)
Seq.zip
(indexed |> Seq.choose (fun (i, v) -> if i%5 = 2 then Some v else None))
(indexed |> Seq.choose (fun (i, v) -> if i%5 = 0 then Some v else None))
... or if you want to make the code shroter, you can refactor:
let filterNthElements div rem =
input |> Seq.mapi (fun i s -> i, s)
|> Seq.choose (fun (i, v) -> if i%div = rem then Some v else None)
Seq.zip (filterNthElements 5 2) (filterNthElements 5 0)