#sliding-window #structure #control #iterator #last #items

sliding_window_alt

A structure that holds the last N items pushed to it

3 releases

0.1.2 Jul 8, 2022
0.1.1 Apr 12, 2022
0.1.0 Apr 2, 2022

#1255 in Data structures

Unlicense

17KB
307 lines

What

This is a sliding window data structure. Inside it there is a vector that has at position 0 the newest element and at position capacity the oldest. Whenever you push an item, said item is stored at position 0, everything shifts, and the last position is forgotten.

For some speed gain, this is not how it internally works. Internally, it has an index that moves, always pointing to the oldest element in the set. This way insertions are O(1).

You can get the information out in three ways, by getting an iterator, by getting a Vec<T>, or by directly indexing

Why

The following crates are fairly similar:

Queues has the type CircularBuffer. It has several problems. The first one is that it has O(n) insertions. The way less worrying one is that it only get the newest item back. On the other hand, it expresses the operations of an abstract queue as a trait, which is good. It has no testing. This is most probably not what you were looking for when you searched for a sliding window.

circular_queue is pretty much the same as this crate, except that this crate doesn't provide several possible orders. On the other hand this create provides more ways of creating and pushing items, more comparisons, and a vector return. It doesn't allow indexing, which this crate does, mostly inspired by...

sliding_window has a similar functionality as circular_queue, but is no_std and has some unsafe code. It allows indexing where 0 is the oldest. This is probably the crate to go for speed and portability.

However, I have published this crate. My reasons are:

  1. It is always filled from the creation, always returning iterators and vectors of the same size. This is specially useful for some mathematical manipulations. This would be a breaking change for the other crates, and is the reason there is a new crate instead of a contribution to those crates.

  2. It adds on to circular_queue indexing, vector return, and a bunch of PartialEqs implementations.

  3. It is 100% safe.

  4. Has no dependencies and is fast to build, (it's a small target, where actual LOC are about 130).

How

There are several examples in the examples folder. Here is some code to get a feel for the library.

use sliding_window_alt::SlidingWindow;

fn main() {
    // Stores the useful information for a model of a system. In this case the
    // latest 5 outputs of the system
    let mut sys = SlidingWindow::new(5, 0.0);

    // caracteristical polynomial of the system, it's stable
    let carac_pol = [0.5, -0.4, 0.2, -0.3, 0.05];

    for _ in 0..=100 {
        sys.push(
            sys.iter()
                .zip(carac_pol)
                .map(|(item, coef)| coef * *item)
                .sum::<f64>() // Multiplies the polinomial
                + 1.0, // the action input, in this case a step
        );
        println!("{}", sys[0]);
    }
}

Declaring

There are three ways of creating a sliding window. One with new, and two froms. You can create from an array, (not Vec!!) or a slice.

Converting

Both &SlidingWindow and SlidingWindow into_iter methods allow for use in for loops, however, both of these methods are O(n).

Pushing

The push method pushes one item and the push_slice an array of items, being the item at position 0 the "youngest" of the items.

Iterating

The methods iter and iter_mut provide iterators that start at the newest item.

Indexing

You can access the contents of your sliding window by indexing, it. 0 is the newest element and capacity is the oldest.

Vector

If you need to perform some analysis for your application, you can obtain a vector from the to_vec method. This operation is O(n), use it when you genuinely need a vector.

Benchmarks

There are some benchmarks for the code and a comparison with the alternative crates presented earlier.

Criteria Wins Loses
Creation Queues/Sliding Windows (?[^1]) this crate
Insertion this crate Queues[^2]
Iteration this crate/ circular_queue sliding_window
Full workflow (initialization not required) sliding_window circular_queue
Full workflow (initialization required) this crate sliding_window

My recommendation is that if you don't require a fixed length, use sliding_window, else, use this one. CircularBuffer from the Queues crate is unlikely to optimally solve your problems.

[^1]: Unknown, because I'm unable to blackbox the amount of items on creation. [^2]: Queues does not offer iterators nor ways to access all the data in the queue and therefore can't compete in the other categories.

No runtime deps