cs-utils 0.21.1

Common utilities.
Documentation

/// Drain and filter at the same time, accepts a `mutable vector ref` and `predicate`.
/// The `predicate` receives a `vector element ref` for the current iteration and
/// in-place removes the element from the vector, if `predicate` returns `true`.
/// Returns and array of `removed` elements back.
/// 
/// ## Examples
/// 
/// ```
/// use cs_utils::{drain_filter, random_bool};
/// 
/// let mut test_vector = vec![
///     random_bool(),
///     random_bool(),
///     random_bool(),
///     random_bool(),
///     random_bool(),
///     random_bool(),
///     random_bool(),
///     random_bool(),
///     random_bool(),
///     random_bool(),
///     random_bool(),
/// ];
/// 
/// let removed_items = drain_filter(
///     &mut test_vector,
///     |el| {
///         return *el;
///     },
/// );
/// 
/// assert!(
///     !removed_items.contains(&false),
///     "Removed items must not contain \"false\" values.",
/// );
/// 
/// assert!(
///     !test_vector.contains(&true),
///     "Removed items must not contain \"true\" values.",
/// );
/// ```
pub fn drain_filter<T>(vector: &mut Vec<T>, mut predicate: impl FnMut(&T) -> bool) -> Vec<T> {
    let mut result = vec![];

    let mut i = 0;
        while i < vector.len() {
            let item = &vector[i];
            if predicate(item) {
                let removed_item = vector.swap_remove(i);
                result.push(removed_item);
                // since we swapped with the last vector item, don't increment
                // the index, - the swapped item must be checked too
                continue;
            }

            i += 1;
        }

    return result;
}

#[cfg(test)]
mod tests {
    use crate::drain_filter;

    #[test]
    fn drains_the_vector() {
        let mut test_vector = vec![
            true,
            false,
            false,
            true,
            true,
            false,
            false,
            false,
        ];

        let removed = drain_filter(&mut test_vector, |el| {
            return *el;
        });

        assert_eq!(
            removed.len(),
            3,
            "Must remove all elements equal to \"true\"."
        );

        assert!(
            !removed.contains(&false),
            "Removed elements vector must not contain \"false\" values."
        );

        assert_eq!(
            test_vector.len(),
            5,
            "Original vector must retain \"false\" values."
        );

        assert!(
            !test_vector.contains(&true),
            "Original vector must not contain \"true\" values."
        );
    }

    #[test]
    fn works_with_non_clone_items() {
        struct TestStruct {
            pub flag: bool,
        }

        let mut test_vector = vec![
            TestStruct { flag: false },
            TestStruct { flag: true },
            TestStruct { flag: false },
            TestStruct { flag: true },
            TestStruct { flag: false },
            TestStruct { flag: true },
            TestStruct { flag: false },
            TestStruct { flag: true },
            TestStruct { flag: true },
        ];

        let removed = drain_filter(&mut test_vector, |el| {
            return el.flag;
        });

        assert_eq!(
            removed.len(),
            5,
            "Must remove all elements equal to \"true\"."
        );

        assert_eq!(
            test_vector.len(),
            4,
            "Must retain all elements equal to \"false\"."
        );
    }
}