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!

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.