views:

642

answers:

3

I have a list of email addresses, and I need to send an email notification to each address. I'd like to do this in blocks of 25 addresses at a time. Is there any quick way in a functional language like F# to "fold" (concatenate) 25 emails addresses together... each separated by a semicolon. I know there is the String.Split method in .NET, but I need to concat 25 at a time.

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

A: 

Its not overly 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))
Juliet
+1  A: 

Here's a way to break into groups of at most 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 solution is just something like

let Sendmail addr = printf "%A\n\n" addr
let allMails = [for i in 1..25 -> sprintf "a%[email protected]" i]
allMails 
|> Break 5
|> Seq.map (fun arr -> System.String.Join(";", arr))
|> Seq.iter Sendmail
Brian
A: 

The Seq module has a windowed function, but that gives you a sliding window, so no good for this question.

Here's my version, I haven't tested how the performance compares with other answers, but it's 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 n s
                    yield! chunks n (Seq.skip n s)           
            }

//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

You could maybe look at Array slices too.

Benjol