Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deprecate group_by in favor of chunk_by #866

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 14 additions & 14 deletions benches/bench1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -390,37 +390,37 @@ fn zip_unchecked_counted_loop3(c: &mut Criterion) {
});
}

fn group_by_lazy_1(c: &mut Criterion) {
fn chunk_by_lazy_1(c: &mut Criterion) {
let mut data = vec![0; 1024];
for (index, elt) in data.iter_mut().enumerate() {
*elt = index / 10;
}

let data = black_box(data);

c.bench_function("group by lazy 1", move |b| {
c.bench_function("chunk by lazy 1", move |b| {
b.iter(|| {
for (_key, group) in &data.iter().group_by(|elt| **elt) {
for elt in group {
for (_key, chunk) in &data.iter().chunk_by(|elt| **elt) {
for elt in chunk {
black_box(elt);
}
}
})
});
}

fn group_by_lazy_2(c: &mut Criterion) {
fn chunk_by_lazy_2(c: &mut Criterion) {
let mut data = vec![0; 1024];
for (index, elt) in data.iter_mut().enumerate() {
*elt = index / 2;
}

let data = black_box(data);

c.bench_function("group by lazy 2", move |b| {
c.bench_function("chunk by lazy 2", move |b| {
b.iter(|| {
for (_key, group) in &data.iter().group_by(|elt| **elt) {
for elt in group {
for (_key, chunk) in &data.iter().chunk_by(|elt| **elt) {
for elt in chunk {
black_box(elt);
}
}
Expand All @@ -436,8 +436,8 @@ fn slice_chunks(c: &mut Criterion) {

c.bench_function("slice chunks", move |b| {
b.iter(|| {
for group in data.chunks(sz) {
for elt in group {
for chunk in data.chunks(sz) {
for elt in chunk {
black_box(elt);
}
}
Expand All @@ -453,8 +453,8 @@ fn chunks_lazy_1(c: &mut Criterion) {

c.bench_function("chunks lazy 1", move |b| {
b.iter(|| {
for group in &data.iter().chunks(sz) {
for elt in group {
for chunk in &data.iter().chunks(sz) {
for elt in chunk {
black_box(elt);
}
}
Expand Down Expand Up @@ -813,8 +813,8 @@ criterion_group!(
zipdot_i32_unchecked_counted_loop,
zipdot_f32_unchecked_counted_loop,
zip_unchecked_counted_loop3,
group_by_lazy_1,
group_by_lazy_2,
chunk_by_lazy_1,
chunk_by_lazy_2,
slice_chunks,
chunks_lazy_1,
equal,
Expand Down
6 changes: 3 additions & 3 deletions examples/iris.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,15 +77,15 @@ fn main() {
let mut plot_symbols = "+ox".chars().cycle();
let mut symbolmap = HashMap::new();

// using Itertools::group_by
for (species, species_group) in &irises.iter().group_by(|iris| &iris.name) {
// using Itertools::chunk_by
for (species, species_chunk) in &irises.iter().chunk_by(|iris| &iris.name) {
// assign a plot symbol
symbolmap
.entry(species)
.or_insert_with(|| plot_symbols.next().unwrap());
println!("{} (symbol={})", species, symbolmap[species]);

for iris in species_group {
for iris in species_chunk {
// using Itertools::format for lazy formatting
println!("{:>3.1}", iris.data.iter().format(", "));
}
Expand Down
30 changes: 17 additions & 13 deletions src/groupbylazy.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use alloc::vec::{self, Vec};
use std::cell::{Cell, RefCell};

/// A trait to unify `FnMut` for `GroupBy` with the chunk key in `IntoChunks`
/// A trait to unify `FnMut` for `ChunkBy` with the chunk key in `IntoChunks`
trait KeyFunction<A> {
type Key;
fn call_mut(&mut self, arg: A) -> Self::Key;
Expand Down Expand Up @@ -282,10 +282,14 @@ where
}
}

/// `GroupBy` is the storage for the lazy grouping operation.
#[deprecated(note = "Use `ChunkBy` instead", since = "0.13.0")]
/// See [`ChunkBy`](crate::structs::ChunkBy).
pub type GroupBy<K, I, F> = ChunkBy<K, I, F>;

/// `ChunkBy` is the storage for the lazy grouping operation.
///
/// If the groups are consumed in their original order, or if each
/// group is dropped without keeping it around, then `GroupBy` uses
/// group is dropped without keeping it around, then `ChunkBy` uses
/// no allocations. It needs allocations only if several group iterators
/// are alive at the same time.
///
Expand All @@ -294,9 +298,9 @@ where
/// value. It should be stored in a local variable or temporary and
/// iterated.
///
/// See [`.group_by()`](crate::Itertools::group_by) for more information.
/// See [`.chunk_by()`](crate::Itertools::chunk_by) for more information.
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
pub struct GroupBy<K, I, F>
pub struct ChunkBy<K, I, F>
where
I: Iterator,
{
Expand All @@ -307,12 +311,12 @@ where
}

/// Create a new
pub fn new<K, J, F>(iter: J, f: F) -> GroupBy<K, J::IntoIter, F>
pub fn new<K, J, F>(iter: J, f: F) -> ChunkBy<K, J::IntoIter, F>
where
J: IntoIterator,
F: FnMut(&J::Item) -> K,
{
GroupBy {
ChunkBy {
inner: RefCell::new(GroupInner {
key: f,
iter: iter.into_iter(),
Expand All @@ -329,7 +333,7 @@ where
}
}

impl<K, I, F> GroupBy<K, I, F>
impl<K, I, F> ChunkBy<K, I, F>
where
I: Iterator,
{
Expand All @@ -348,7 +352,7 @@ where
}
}

impl<'a, K, I, F> IntoIterator for &'a GroupBy<K, I, F>
impl<'a, K, I, F> IntoIterator for &'a ChunkBy<K, I, F>
where
I: Iterator,
I::Item: 'a,
Expand All @@ -368,7 +372,7 @@ where
/// Iterator element type is `(K, Group)`:
/// the group's key `K` and the group's iterator.
///
/// See [`.group_by()`](crate::Itertools::group_by) for more information.
/// See [`.chunk_by()`](crate::Itertools::chunk_by) for more information.
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
pub struct Groups<'a, K, I, F>
where
Expand All @@ -377,7 +381,7 @@ where
K: 'a,
F: 'a,
{
parent: &'a GroupBy<K, I, F>,
parent: &'a ChunkBy<K, I, F>,
}

impl<'a, K, I, F> Iterator for Groups<'a, K, I, F>
Expand Down Expand Up @@ -418,7 +422,7 @@ where
K: 'a,
F: 'a,
{
parent: &'a GroupBy<K, I, F>,
parent: &'a ChunkBy<K, I, F>,
index: usize,
first: Option<I::Item>,
}
Expand Down Expand Up @@ -476,7 +480,7 @@ where

/// `ChunkLazy` is the storage for a lazy chunking operation.
///
/// `IntoChunks` behaves just like `GroupBy`: it is iterable, and
/// `IntoChunks` behaves just like `ChunkBy`: it is iterable, and
/// it only buffers if several chunk iterators are alive at the same time.
///
/// This type implements [`IntoIterator`] (it is **not** an iterator
Expand Down
37 changes: 26 additions & 11 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
//! - `use_std`
//! - Enabled by default.
//! - Disable to compile itertools using `#![no_std]`. This disables
//! any items that depend on collections (like `group_by`, `unique`,
//! any items that depend on collections (like `chunk_by`, `unique`,
//! `kmerge`, `join` and many more).
//!
//! ## Rust Version
Expand Down Expand Up @@ -99,8 +99,11 @@
pub use crate::exactly_one_err::ExactlyOneError;
pub use crate::flatten_ok::FlattenOk;
pub use crate::format::{Format, FormatWith};
#[allow(deprecated)]
#[cfg(feature = "use_alloc")]
pub use crate::groupbylazy::GroupBy;
#[cfg(feature = "use_alloc")]
pub use crate::groupbylazy::{Chunk, Chunks, Group, GroupBy, Groups, IntoChunks};
pub use crate::groupbylazy::{Chunk, ChunkBy, Chunks, Group, Groups, IntoChunks};
#[cfg(feature = "use_std")]
pub use crate::grouping_map::{GroupingMap, GroupingMapBy};
pub use crate::intersperse::{Intersperse, IntersperseWith};
Expand Down Expand Up @@ -575,10 +578,10 @@
/// Consecutive elements that map to the same key (“runs”), are assigned
/// to the same group.
///
/// `GroupBy` is the storage for the lazy grouping operation.
/// `ChunkBy` is the storage for the lazy grouping operation.
///
/// If the groups are consumed in order, or if each group's iterator is
/// dropped without keeping it around, then `GroupBy` uses no
/// dropped without keeping it around, then `ChunkBy` uses no
/// allocations. It needs allocations only if several group iterators
/// are alive at the same time.
///
Expand All @@ -593,20 +596,20 @@
/// ```
/// use itertools::Itertools;
///
/// // group data into runs of larger than zero or not.
/// // chunk data into runs of larger than zero or not.
/// let data = vec![1, 3, -2, -2, 1, 0, 1, 2];
/// // groups: |---->|------>|--------->|
/// // chunks: |---->|------>|--------->|
///
/// // Note: The `&` is significant here, `GroupBy` is iterable
/// // Note: The `&` is significant here, `ChunkBy` is iterable
/// // only by reference. You can also call `.into_iter()` explicitly.
/// let mut data_grouped = Vec::new();
/// for (key, group) in &data.into_iter().group_by(|elt| *elt >= 0) {
/// data_grouped.push((key, group.collect()));
/// for (key, chunk) in &data.into_iter().chunk_by(|elt| *elt >= 0) {
/// data_grouped.push((key, chunk.collect()));
/// }
/// assert_eq!(data_grouped, vec![(true, vec![1, 3]), (false, vec![-2, -2]), (true, vec![1, 0, 1, 2])]);
/// ```
#[cfg(feature = "use_alloc")]
fn group_by<K, F>(self, key: F) -> GroupBy<K, Self, F>
fn chunk_by<K, F>(self, key: F) -> ChunkBy<K, Self, F>
where
Self: Sized,
F: FnMut(&Self::Item) -> K,
Expand All @@ -615,13 +618,25 @@
groupbylazy::new(self, key)
}

/// See [`.chunk_by()`](Itertools::chunk_by).
#[deprecated(note = "Use .chunk_by() instead", since = "0.13.0")]
#[cfg(feature = "use_alloc")]
fn group_by<K, F>(self, key: F) -> ChunkBy<K, Self, F>
where
Self: Sized,
F: FnMut(&Self::Item) -> K,
K: PartialEq,
{
self.chunk_by(key)
}

Check warning on line 631 in src/lib.rs

View check run for this annotation

Codecov / codecov/patch

src/lib.rs#L624-L631

Added lines #L624 - L631 were not covered by tests

/// Return an *iterable* that can chunk the iterator.
///
/// Yield subiterators (chunks) that each yield a fixed number elements,
/// determined by `size`. The last chunk will be shorter if there aren't
/// enough elements.
///
/// `IntoChunks` is based on `GroupBy`: it is iterable (implements
/// `IntoChunks` is based on `ChunkBy`: it is iterable (implements
/// `IntoIterator`, **not** `Iterator`), and it only buffers if several
/// chunk iterators are alive at the same time.
///
Expand Down
42 changes: 21 additions & 21 deletions tests/quick.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1053,58 +1053,58 @@ quickcheck! {
}

quickcheck! {
fn fuzz_group_by_lazy_1(it: Iter<u8>) -> bool {
fn fuzz_chunk_by_lazy_1(it: Iter<u8>) -> bool {
let jt = it.clone();
let groups = it.group_by(|k| *k);
itertools::equal(jt, groups.into_iter().flat_map(|(_, x)| x))
let chunks = it.chunk_by(|k| *k);
itertools::equal(jt, chunks.into_iter().flat_map(|(_, x)| x))
}
}

quickcheck! {
fn fuzz_group_by_lazy_2(data: Vec<u8>) -> bool {
let groups = data.iter().group_by(|k| *k / 10);
let res = itertools::equal(data.iter(), groups.into_iter().flat_map(|(_, x)| x));
fn fuzz_chunk_by_lazy_2(data: Vec<u8>) -> bool {
let chunks = data.iter().chunk_by(|k| *k / 10);
let res = itertools::equal(data.iter(), chunks.into_iter().flat_map(|(_, x)| x));
res
}
}

quickcheck! {
fn fuzz_group_by_lazy_3(data: Vec<u8>) -> bool {
let grouper = data.iter().group_by(|k| *k / 10);
let groups = grouper.into_iter().collect_vec();
let res = itertools::equal(data.iter(), groups.into_iter().flat_map(|(_, x)| x));
fn fuzz_chunk_by_lazy_3(data: Vec<u8>) -> bool {
let grouper = data.iter().chunk_by(|k| *k / 10);
let chunks = grouper.into_iter().collect_vec();
let res = itertools::equal(data.iter(), chunks.into_iter().flat_map(|(_, x)| x));
res
}
}

quickcheck! {
fn fuzz_group_by_lazy_duo(data: Vec<u8>, order: Vec<(bool, bool)>) -> bool {
let grouper = data.iter().group_by(|k| *k / 3);
let mut groups1 = grouper.into_iter();
let mut groups2 = grouper.into_iter();
fn fuzz_chunk_by_lazy_duo(data: Vec<u8>, order: Vec<(bool, bool)>) -> bool {
let grouper = data.iter().chunk_by(|k| *k / 3);
let mut chunks1 = grouper.into_iter();
let mut chunks2 = grouper.into_iter();
let mut elts = Vec::<&u8>::new();
let mut old_groups = Vec::new();
let mut old_chunks = Vec::new();

let tup1 = |(_, b)| b;
for &(ord, consume_now) in &order {
let iter = &mut [&mut groups1, &mut groups2][ord as usize];
let iter = &mut [&mut chunks1, &mut chunks2][ord as usize];
match iter.next() {
Some((_, gr)) => if consume_now {
for og in old_groups.drain(..) {
for og in old_chunks.drain(..) {
elts.extend(og);
}
elts.extend(gr);
} else {
old_groups.push(gr);
old_chunks.push(gr);
},
None => break,
}
}
for og in old_groups.drain(..) {
for og in old_chunks.drain(..) {
elts.extend(og);
}
for gr in groups1.map(&tup1) { elts.extend(gr); }
for gr in groups2.map(&tup1) { elts.extend(gr); }
for gr in chunks1.map(&tup1) { elts.extend(gr); }
for gr in chunks2.map(&tup1) { elts.extend(gr); }
itertools::assert_equal(&data, elts);
true
}
Expand Down
Loading
Loading