How do I get a specific number of items from a collection containing the repeated items from another collection?

Given a collection of items, how do I return a (possibly repeated) collection containing those individual items, in the order they are given in the original collection, but limited to a specific total number of items?

Apologies for the weird wording of the question but I couldn’t think of a more succinct way of saying it.

This is my first time asking a question in the forums and I’ve still got a lot to learn about F# (again).

With this definition:

type Thingy =
 | Doodah
 | Whatsit

I would like to create a collection (doesn’t need to be a sequence) of items, such as
let items = seq { Doodah; Whatsit } // Number of items must be at least one.

With a ‘limiting value’ such as:
let number = 7 // Could be any integer value greater than zero.

And then I would like to create a collection (preferably a sequence but doesn’t have to be) which would be as follows:
{ Doodah; Whatsit; Doodah; Whatsit; Doodah; Whatsit; Doodah } // Note: There’s no ‘Whatsit’ at the end, 7 items only.
… where the number of items in the resultant collection is the number in the number value.

I’ve managed to cobble together this code:
let oneByOne items number = seq {for _ in 1..number do yield! items } |> Seq.take number
… which works, but I think it looks ‘nasty’ (and I’m worried that it could be ‘generating’ more values than it returns, but don’t know how to check).

Is there a better way of doing this?
Is there a ‘shortcut’ version when I know there will only be two items in the original collection?
Is there a particular set of functions that I should be learning more about for this sort of thing?

I’ve done quite a bit of web searching but can’t find anything relevant.

Welcome GarryP!

It’s actually pretty easy to check what values are being generated, by just creating a seq that prints on each value:

let xs = seq {
  for i in 1 .. 2 do
    printfn "producing %i" i
    yield i
}

then you can see

> oneByOne xs 7 |> Seq.toArray;;
producing 1
producing 2
producing 1
producing 2
producing 1
producing 2
producing 1
val it: int[] = [|1; 2; 1; 2; 1; 2; 1|]

So you can see it produced no extra values than it needed. (Note you need to do Seq.toArray since the results of oneByOne itself is lazy, and converting it to an array forces all of the elements to evaluate).

Personally, I think the way you did it looks really nice, and idiomatic for F# (though it would be a bit more idiomatic to reverse the order of arguments, so you could do items |> oneByOne 7).
Maybe it would look nice to make an infinite sequence of repeated elements, since it’s a little funny to go from 1 .. number in your oneByOne implementation, since that’s a fairly arbitrary upper bound. You could do

let cycle xs = seq { for _ in Seq.initInfinite id do yield! xs }
let oneByOne number items = cycle items |> Seq.take number

But there’s no performance difference between that and your implementation, and it’s arguable whether the implementation is “nicer” in any way.

Learning the functions in the Seq module is really the only thing that I can think of that would be worth learning for this sort of thing. e.g., in other Functional Programming languages the cycle function already exists and that would have simplified the solution, but in this case F# just happened to not have that function pre-defined.

Thanks for the welcome and the advice.

I’ve made the changes to my code and it looks much better now:

let cycle originalSequence = seq { for _ in Seq.initInfinite id do yield! originalSequence }
cycle (seq { Doodah ; Whatsit }) |> Seq.take 7

Seq.initInfinite also allowed me to change:

let repeatedSingle item numberOfItems = seq {for _ in 1..numberOfItems -> item}
7 |> repeatedSingle Doodah

…into:

let endless item = Seq.initInfinite (fun (_) -> item)
endless Doodah |> Seq.take 7

…which, I think, is a lot nicer.

Looks like I’m going to have to read a lot more about Seq.

Cheers.

P.S. I have no idea why the editor in this site has decided to colour some of the code differently to the other code.

1 Like