F # - splitting a list into concatenated strings per interval

I have a list of email addresses and I need to send an email notification to each address. I would like to do this in blocks of 25 addresses at a time. Is there a quick way in a functional language such as F # to β€œcollapse” (concatenate) 25 email addresses together ... each one is separated by a semicolon. I know there is a String.Split method in .NET, but I need to concatenate 25 at a time.

What is the most elegant way to accomplish this in F #?

+1
list concatenation fold f #
source share
4 answers

Here you can divide into groups no more than N:

// break a sequence up into a sequence of arrays, // each of length at most 'n' let Break n (s:seq<_>) = seq { use e = s.GetEnumerator() while e.MoveNext() do let i = ref 0 yield [| yield e.Current i := !i + 1 while !i < n && e.MoveNext() do yield e.Current i := !i + 1 |] } 

and then your decision is just something like

 let Sendmail addr = printf "%A\n\n" addr let allMails = [for i in 1..25 -> sprintf "a% i@example.com " i] allMails |> Break 5 |> Seq.map (fun arr -> System.String.Join(";", arr)) |> Seq.iter Sendmail 
+1
source share

If this is still relevant, here is a function to concatenate a string sequence using a delimiter, but without conversion to an array.

 open System open System.Text /// Join a sequence of strings using a delimiter. /// Equivalent to String.Join() but without arrays. let join (items : seq<string>) (delim : string) = // Collect the result in the string builder buffer // The end-sequence will be "item1,delim,...itemN,delim" let buff = Seq.fold (fun (buff :StringBuilder) (s:string) -> buff.Append(s).Append(delim)) (new StringBuilder()) items // We don't want the last delim in the result buffer, remove buff.Remove(buff.Length-delim.Length, delim.Length).ToString() 
+1
source share

Not too pretty, but it works:

 let breakWords separator groupSize (input : string) = let words = input.Split([| separator |]) let arraySize = words.Length / groupSize + (if words.Length % groupSize > 0 then 1 else 0) let groups = Array.init arraySize (fun index -> let startIndex = index * groupSize let endIndex = Math.Min(startIndex + groupSize - 1, words.Length - 1) words.[startIndex .. endIndex]) groups |> Seq.map (fun x -> String.Join(";", x)) 
0
source share

The Seq module has a window function, but it gives you a sliding window, so it is not suitable for this question.

Here is my version, I have not tested how performance is compared with other answers, but it is prettier (I think!)

 //Add an "extension function" to the Seq module module Seq = let rec chunks n (s:#seq<_>) = seq { if Seq.length s <= n then yield s else yield Seq.take ns yield! chunks n (Seq.skip ns) } //Make curried version of String.Join let join sep (s:#seq<string>) = System.String.Join(sep, Array.of_seq s) //Compose the bits together let mailMerge n = Seq.chunks n >> Seq.map (join ";") //Test mailList |> mailMerge 25 

Perhaps you can look at the fragments of the array .

0
source share

All Articles