How to merge an alternating bool into a map iteration

I have a BTreeMap which I’m iterating using rayon like this:

// info is from the caller
image_for_id.par_iter().map(|(id, image)|) { draw(&info, &id, &image) }).collect::<Result<()>>()

I want to change the closure call to include a ‘flipper’:

let mut flipper = true;
// I want flipper to be true for the first one, then false, then true, then false, alternately
image_for_id.par_iter().map(|(id, image)|) { draw(&info, &id, &image, &flipper) }).collect::<Result<()>>()

Can someone point me to an example or show me how to do this please?

You may be interested in rayon::iter::split(), which is a reasonably straightforward way to roll your own parallel iterator.

General idea would be to build a struct like this…

struct FlipperIter {
    first: bool,
    length: usize,
}

…and a way to split it in two (setting the right value of first on the “right-hand” side depending on if “length” is even or odd).

Then you would need to couple it with a BTreeMap iterator/splitter.

There may be an easier way to go about this, though.

I’m not sure about rayon but you can create an iterator that cycles two values using iter.cycle:

let flipper_iter = [true, false].iter().cloned().cycle();

Then you could zip the two iterators together before you map.

3 Likes

By default this is hard to do because rayon’s iterator for BTreeMap doesn’t implement IndexedParallelIterator and without it you can’t zip or enumerate, so the solution would have been a custom iterator. But making a custom iterator for a collection that isn’t in your files is a pain in the butt (if even possible).
So how rayon does it? By collecting the BTreeMap into a Vec and then iterating over it. Why can’t you use IndexedParallelIterator then? I have no idea.
Now the solution is way easier, we’ll just collect it ourselves and iterate. Something like this:

let vec_iter = image_for_id.iter().zip(vec![true, false].into_iter().cycle()).collect::<Vec<_>>();
vec_iter
    .into_par_iter()
    .map(|((id, image), flipper)| draw(id, image, flipper))
    .collect::<Result<()>>();
1 Like

The idea is that collecting into a Vec is an implementation detail, but someday we might find a way to iterate more directly. For instance, maybe there could be a way to directly split btree ranges, but that probably wouldn’t have exact sub-counts to be properly indexed. So we keep that out of the API as a way of future-proofing.

1 Like

That makes perfect sense, thank you.

Thank you, zipping true, false with cycle and then mapping worked very well!