How do I use a Peekable iterator in a filter closure? - iterator

I need to find only the numbers where the next number is the same: [1,2,2,3,4,4] should produce [2,4]. Since I need to peek at the next number, I figured I'd try out using a Peekable iterator and write a filter.
fn main() {
let xs = [1, 2, 2, 3, 4, 4];
let mut iter = xs.iter().peekable();
let pairs = iter.filter(move |num| {
match iter.peek() {
Some(next) => num == next,
None => false,
}
});
for num in pairs {
println!("{}", num);
}
}
I get an error:
error[E0382]: capture of moved value: `iter`
--> src/main.rs:6:15
|
5 | let pairs = iter.filter(move |num| {
| ---- value moved here
6 | match iter.peek() {
| ^^^^ value captured here after move
|
= note: move occurs because `iter` has type `std::iter::Peekable<std::slice::Iter<'_, i32>>`, which does not implement the `Copy` trait
I think this is because iter is being used by the closure, but it hasn't borrowed it, and it can't copy it.
How do I solve this problem of wanting to refer to the iterator inside a filter?

refer to the iterator inside a filter
I don't believe you can. When you call filter, it takes ownership of the base iterator:
fn filter<P>(self, predicate: P) -> Filter<Self, P>
where
P: FnMut(&Self::Item) -> bool,
Once you do that, it's gone. There is no more iter. In some similar cases, you can use Iterator::by_ref to mutably borrow the iterator, drive it for a while, then refer back to the original. That won't work in this case because the inner iterator would need to borrow it mutably a second time, which is disallowed.
find only the numbers where the next number is the same.
extern crate itertools;
use itertools::Itertools;
fn main() {
let input = [1, 2, 2, 3, 4, 4];
let pairs = input
.iter()
.tuple_windows()
.filter_map(|(a, b)| if a == b { Some(a) } else { None });
let result: Vec<_> = pairs.cloned().collect();
assert_eq!(result, [2, 4]);
}
Or if you wanted something using only the standard library:
fn main() {
let xs = [1, 2, 2, 3, 4, 4];
let mut prev = None;
let pairs = xs.iter().filter_map(move |curr| {
let next = if prev == Some(curr) { Some(curr) } else { None };
prev = Some(curr);
next
});
let result: Vec<_> = pairs.cloned().collect();
assert_eq!(result, [2, 4]);
}

Related

Merge and order two streams using Kotlin flow

I have two streams where each stream has a different set of values and a different amount:
runBlocking {
val flowA = flow {
mutableListOf<Int>(0, 4, 9).forEach {
emit(it)
}
}
val flowB = flow {
mutableListOf<Int>(1, 2, 3, 5, 6, 7, 8).forEach {
emit(it)
}
}
merge(flowA, flowB).collect{
Log.i(TAG, it.toString())
}
}
Is it possible to use Kotlin's Flow to merge these two streams so that the result is sorted? So the collected values should end up being:
0, 1, 2, 3, 4, 5, 6, 7, 8, 9
The values in each stream is already sorted. I just need to merge them. One very important thing however. I don't want to sort AFTER all the values have been collected. The sorting must be done as each value is emitted. My sample above is over simplified. In the real app, the source for each flow contains large arrays for each item. Waiting for all the values to be collected and then sorting is unacceptable as this would require a large amount of memory. But the basic concept for simple integer values should work for more complex data types as well.
Maybe the filter operator is what I need but that isn't clear as I have little experience with flows.
Disclaimer: This is the first time I've used Flow.
Even though the streams are "already sorted", it seems you cannot control the timing the elements will arrive from the two streams. So, you will only be able to get an ordered list by collecting all the elements, then sorting them.
This worked for me:
val sortedResults = flowA
.onCompletion { emitAll(flowB) }
.toCollection(mutableListOf())
.sorted()
println(sortedResults)
Output:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
You could use the combine operator to get value from two or more flows and then a flatMapLatest operator like this:
val desiredFlow = combine(getFlowA(),getFlowB()) { a, b ->
val arr = IntArray(a.size + b.size)
var i = 0
var j = 0
var k = 0
while (i < a.size && j < b.size)
arr[k++] = if (a[i] < b[j]) a[i++] else b[j++]
while (i < a.size)
arr[k++] = a[i++]
while (j < b.size)
arr[k++] = b[j++]
arr
}.flatMapLatest { result ->
flow {
emit(result.toMutableList())
}
}
fun getFlowA(): Flow<MutableList<Int>> {
return flow {
emit(mutableListOf<Int>(0,4,9))
}
}
fun getFlowB(): Flow<MutableList<Int>> {
return flow {
emit(mutableListOf(1,2,3,4,5,6,7,8))
}
}
I'm from the Android dev world and not expert with Flows so kindly pardon me if isn't what you expected, but this produces the final output as:
[0, 1, 2, 3, 4, 4, 5, 6, 7, 8, 9]
After going through the question, I have two ideas either using a flatten merge or using a delay.
The first idea looks something like this.
runBlocking {
val flowA = flow {
mutableListOf<Int>(0, 4, 9).forEach {
emit(it)
}
}
val flowB = flow {
mutableListOf<Int>(1, 2, 3, 5, 6, 7, 8).forEach {
emit(it)
}
}
val newList = mutableListOf<Int>()
val a = flowOf(flowA, flowB).flattenMerge().collect { value ->
when {
newList.isEmpty() -> newList.add(value)
newList.last() <= value -> newList.add(value)
newList.last() > value -> {
//sorting values as they arrive
val i = newList.lastIndex
newList.add(value)
val newValue = newList[i]
newList[i] = newList[i + 1]
newList[i + 1] = newValue
}
}
}
Log.i(TAG, newList.toString())
}
In the second one, add appropriate delays to your first 2 flows.
PS:-
Android Studio gives a warning while using flattenMerge.
This declaration is in a preview state and can be changed in a backwards-incompatible manner with a best-effort migration. Its usage should be marked with '#kotlinx.coroutines.FlowPreview' or '#OptIn(kotlinx.coroutines.FlowPreview::class)' if you accept the drawback of relying on preview API
I don't think you can do this using the built-in flow operators, but you should certainly be able to implement your own. You can use channelFlow for that purpose. This is a versatile way to build a flow that gives us a coroutine scope to work in, and lets us emit items by sending to a channel.
fun <T> mergeOrdered(flowA: Flow<T>, flowB: Flow<T>) = channelFlow {
val channelA = flowA.produceIn(this)
val channelB = flowB.produceIn(this)
var a = channelA.receive()
var b = channelB.receive()
while (isActive) {
if (a < b) {
send(a)
a = channelA.receive()
} else {
send(b)
b = channelB.receive()
}
}
}
This simple example doesn't handle what happens when flowA and flowB run out of elements, but that should be easy enough to add.

Mutating a value in an array list in Zig

Noob question:
I want to mutate a value that exists in an array list. I initially tried to just grab the indexed item and directly change its field value.
const Foo = struct {
const Self = #This();
foo: u8,
};
pub fn main() anyerror!void {
const foo = Foo {
.foo = 1,
};
const allocator = std.heap.page_allocator;
var arr = ArrayList(Foo).init(allocator);
arr.append(foo) catch unreachable;
var a = arr.items[0];
std.debug.warn("a: {}", .{a});
a.foo = 2;
std.debug.warn("a: {}", .{a});
std.debug.warn("arr.items[0]: {}", .{arr.items[0]});
//In order to update the memory in [0] I have to reassign it to a.
//arr.items[0] = a;
}
However, the result is unexpected to me:
a: Foo{ .foo = 1 }
a: Foo{ .foo = 2 }
arr.items[0]: Foo{ .foo = 1 }
I would have thought that arr.items[0] would now equal Foo{ .foo = 2 }.
This is probably because I misunderstand slices.
Does a not point to the same memory as arr.items[0]?
Does arr.items[0] return a pointer to a copied item?
var a = arr.items[0];
That is making a copy of the item in arr.items[0].
If you want a reference, write var a = &arr.items[0]; instead.

Return Option inside Loop

The program aims to use a loop to check if the index of a iterator variable meets certain criteria (i.g., index == 3). If find the desired index, return Some(123), else return None.
fn main() {
fn foo() -> Option<i32> {
let mut x = 5;
let mut done = false;
while !done {
x += x - 3;
if x % 5 == 0 {
done = true;
}
for (index, value) in (5..10).enumerate() {
println!("index = {} and value = {}", index, value);
if index == 3 {
return Some(123);
}
}
return None; //capture all other other possibility. So the while loop would surely return either a Some or a None
}
}
}
The compiler gives this error:
error[E0308]: mismatched types
--> <anon>:7:9
|
7 | while !done {
| ^ expected enum `std::option::Option`, found ()
|
= note: expected type `std::option::Option<i32>`
= note: found type `()`
I think the error source might be that a while loop evaluates to a (), thus it would return a () instead of Some(123). I don't know how to return a valid Some type inside a loop.
The value of any while true { ... } expression is always (). So the compiler expects your foo to return an Option<i32> but finds the last value in your foo body is ().
To fix this, you can add a return None outside the original while loop. You can also use the loop construct like this:
fn main() {
// run the code
foo();
fn foo() -> Option<i32> {
let mut x = 5;
loop {
x += x - 3;
for (index, value) in (5..10).enumerate() {
println!("index = {} and value = {}", index, value);
if index == 3 {
return Some(123);
}
}
if x % 5 == 0 {
return None;
}
}
}
}
The behaviour of while true { ... } statements is maybe a bit quirky and there have been a few requests to change it.

Does Rust have an equivalent to Haskell's `sequence` function?

I am mapping over Vec such that each item becomes a Result. If any element is an Err then I want to terminate early and produce an Err, otherwise I want to produce an Ok containing the Vec<i32>.
let v = (1..10)
.map( |n| {
if n % 4 == 0 {
Err("Too fourish!")
}
else {
Ok(n)
}
}).collect::<Vec<_>>();
println!("{:?}", v);
In Haskell, I can use sequence. Is there an equivalent built-in Rust function?
You can use the FromIterator implementation of Result for this:
fn main() {
let v = (1..10)
.map( |n| {
if n % 4 == 0 {
Err("Too fourish!")
}
else {
Ok(n)
}
}).collect::<Result<Vec<_>, _>>();
println!("{:?}", v);
let v = (1..10)
.map( |n| {
if n % 4 == 4 { // impossible
Err("Too fourish!")
}
else {
Ok(n)
}
}).collect::<Result<Vec<_>, _>>();
println!("{:?}", v);
}
Output:
Err("Too fourish!")
Ok([1, 2, 3, 4, 5, 6, 7, 8, 9])
More info: https://doc.rust-lang.org/std/result/enum.Result.html#implementations

is it possible to filter on a vector in-place?

I'd like to remove some elements from a Vec, but vec.iter().filter().collect() creates a new vector with borrowed items.
I'd like to mutate the original Vec without extra memory allocation (and keep memory of removed elements as an extra capacity of the vector).
If you want to remove elements, you can use retain(), which removes elements from the vector if the closure returns false:
let mut vec = vec![1, 2, 3, 4];
vec.retain(|&x| x % 2 == 0);
assert_eq!(vec, [2, 4]);
If you want to modify the elements in place, you have to do that in a for x in vec.iter_mut().
If you truly want to mutate the vector's elements while filtering it, you can use the nightly-only method Vec::drain_filter, an extremely flexible tool:
#![feature(drain_filter)]
fn main() {
let mut vec = vec![1, 2, 3, 4];
vec.drain_filter(|x| {
if *x % 2 == 0 {
true
} else {
*x += 100;
false
}
});
assert_eq!(vec, [101, 103]);
}
It also allows you to get the removed elements as the return value of the entire method is an iterator!
Till Vec::drain_filter gets stable, we can solve the problem with homebrewed rust:
fn main() {
let mut v = vec![1, 2, 3, 4];
let mut i = 0;
while i < v.len() {
if v[i] % 2 == 0 {
v.remove(i);
} else {
v[i] += 100;
i += 1;
}
}
println!("{:?}", v); // [101, 103]
}
BTW remove() is an O(n) operation but doesn't allocate memory.
Playground
I am providing my take for this problem as I was unaware of the retain method:
impl<T> RemoveFilter<T> for Vec<T> {}
pub trait RemoveFilter<T>: BorrowMut<Vec<T>> {
fn remove_filter<F: for<'b> FnMut(&'b T) -> bool>(&mut self, mut cb: F) {
let vec: &mut Vec<T> = self.borrow_mut();
let mut write_to = 0;
let mut read_from = 0;
while read_from < vec.len() {
let maintain = cb(&mut vec[read_from]);
if maintain {
vec.as_mut_slice().swap(read_from, write_to);
write_to += 1;
}
read_from += 1;
}
vec.resize_with(write_to, || panic!("We are shrinking the vector"));
}
}
It will shift the elements as it iterates and then remove anything that is left behind. I think this is code may easily modified to solve other problems.