pub fn diff_by_key<S, T, K: Eq + Hash>(
    a: impl IntoIterator<Item = S, IntoIter = impl DoubleEndedIterator<Item = S>>,
    mut f: impl FnMut(&S) -> &K,
    b: impl IntoIterator<Item = T, IntoIter = impl DoubleEndedIterator<Item = T>>,
    mut g: impl FnMut(&T) -> &K,
    cb: &mut impl DiffCallback<S, T>,
) {
    let (mut a, mut b) = (a.into_iter().de_peekable(), b.into_iter().de_peekable());

    // Sync nodes with same key at start
    while a
        .peek()
        .and_then(|a| b.peek().filter(|&b| f(a) == g(b)))
        .is_some()
    {
        cb.unchanged(a.next().unwrap(), b.next().unwrap());
    }

    // Sync nodes with same key at end
    while a
        .peek_back()
        .and_then(|a| b.peek_back().filter(|&b| f(a) == g(b)))
        .is_some()
    {
        cb.unchanged(a.next_back().unwrap(), b.next_back().unwrap());
    }

    if a.peek().is_none() {
        return b.rev().for_each(|x| cb.inserted(x)); // If all of a was synced, add remaining in b
    } else if b.peek().is_none() {
        return a.for_each(|x| cb.removed(x)); // If all of b was synced, remove remaining in a
    }

    let (b, mut sources): (Vec<_>, Vec<_>) = b.map(|x| (x, None)).unzip();
    let (mut last_j, mut moved) = (0, false);
    // Associate elements in `a` to their counterparts in `b`, or remove if nonexistant
    let assoc = |(i, (j, a_elem)): (_, (Option<usize>, _))| {
        if let Some(j) = j {
            debug_assert!(sources[j].is_none(), "Duplicate key"); // Catch some instances of dupes
            sources[j] = Some((i, a_elem));

            if j < last_j {
                moved = true;
            }
            last_j = j;
        } else {
            cb.removed(a_elem);
        }
    };
    // If size is small just loop through
    if b.len() < 4 || a.size_hint().1.map_or(false, |hi| hi | b.len() < 32) {
        a.map(|a_elem| (b.iter().position(|b_elem| f(&a_elem) == g(b_elem)), a_elem))
            .enumerate()
            .for_each(assoc);
    } else {
        // Map of keys in b to their respective indices
        let key_index: FxHashMap<_, _> = b.iter().enumerate().map(|(j, ref x)| (g(x), j)).collect();
        a.map(|a_elem| (key_index.get(&f(&a_elem)).copied(), a_elem))
            .enumerate()
            .for_each(assoc);
    }

    if moved {
        // Find longest sequence that can remain stationary
        let mut seq = sources
            .longest_increasing_subsequence_by(
                |a, b| a.as_ref().unwrap().0.cmp(&b.as_ref().unwrap().0),
                Option::is_some,
            )
            .into_iter()
            .rev()
            .peekable();
        for (i, (b, source)) in b.into_iter().zip(sources).enumerate().rev() {
            if let Some((_, a)) = source {
                if Some(&i) == seq.peek() {
                    seq.next();
                    cb.unchanged(a, b);
                } else {
                    cb.moved(a, b);
                }
            } else {
                cb.inserted(b);
            }
        }
    } else {
        for (b, source) in b.into_iter().zip(sources).rev() {
            if let Some((_, a)) = source {
                cb.unchanged(a, b);
            } else {
                cb.inserted(b);
            };
        }
    }
}
/// Computes the difference between the two iterators with key extraction functions.
///
/// Keys have to be unique. After testing for common prefixes and suffixes, returns removals in
/// forward order and then insertions/moves in reverse order. Guaranteed not to allocate if the
/// changeset is entirely contiguous insertions or removals. Result stores `S`/`T`:s instead of
/// indices; use [enumerate] if preferable.
///
/// # Panics
///
/// May panic if `a` contains duplicate keys.
///
/// [enumerate]: std::iter::Iterator::enumerate
Computes the difference between the two iterators with key extraction functions.
Keys have to be unique. After testing for common prefixes and suffixes, returns removals in
forward order and then insertions/moves in reverse order. Guaranteed not to allocate if the
changeset is entirely contiguous insertions or removals. Result stores `S`/`T`:s instead of
indices; use [enumerate] if preferable.
Computes the difference between the two iterators with key extraction functions . Keys have to be unique . After testing for common prefixes and suffixes returns removals in forward order and then insertions / moves in reverse order . Guaranteed not to allocate if the changeset is entirely contiguous insertions or removals . Result stores ` S ` / ` T ` : s instead of indices ; use [ enumerate ] if preferable .
pub fn diff_by_key<S, T, K: Eq + Hash>(
    a: impl IntoIterator<Item = S, IntoIter = impl DoubleEndedIterator<Item = S>>,
    mut f: impl FnMut(&S) -> &K,
    b: impl IntoIterator<Item = T, IntoIter = impl DoubleEndedIterator<Item = T>>,
    mut g: impl FnMut(&T) -> &K,
    cb: &mut impl DiffCallback<S, T>,
) {
    let (mut a, mut b) = (a.into_iter().de_peekable(), b.into_iter().de_peekable());
    // Sync nodes with same key at start
    while a
        .peek()
        .and_then(|a| b.peek().filter(|&b| f(a) == g(b)))
        .is_some()
    {
        cb.unchanged(a.next().unwrap(), b.next().unwrap());
    }
    // Sync nodes with same key at end
    while a
        .peek_back()
        .and_then(|a| b.peek_back().filter(|&b| f(a) == g(b)))
        .is_some()
    {
        cb.unchanged(a.next_back().unwrap(), b.next_back().unwrap());
    }
    if a.peek().is_none() {
        return b.rev().for_each(|x| cb.inserted(x)); // If all of a was synced, add remaining in b
    } else if b.peek().is_none() {
        return a.for_each(|x| cb.removed(x)); // If all of b was synced, remove remaining in a
    }
    let (b, mut sources): (Vec<_>, Vec<_>) = b.map(|x| (x, None)).unzip();
    let (mut last_j, mut moved) = (0, false);
    // Associate elements in `a` to their counterparts in `b`, or remove if nonexistant
    let assoc = |(i, (j, a_elem)): (_, (Option<usize>, _))| {
        if let Some(j) = j {
            debug_assert!(sources[j].is_none(), "Duplicate key"); // Catch some instances of dupes
            sources[j] = Some((i, a_elem));
            if j < last_j {
                moved = true;
            }
            last_j = j;
        } else {
            cb.removed(a_elem);
        }
    };
    // If size is small just loop through
    if b.len() < 4 || a.size_hint().1.map_or(false, |hi| hi | b.len() < 32) {
        a.map(|a_elem| (b.iter().position(|b_elem| f(&a_elem) == g(b_elem)), a_elem))
            .enumerate()
            .for_each(assoc);
    } else {
        // Map of keys in b to their respective indices
        let key_index: FxHashMap<_, _> = b.iter().enumerate().map(|(j, ref x)| (g(x), j)).collect();
        a.map(|a_elem| (key_index.get(&f(&a_elem)).copied(), a_elem))
            .enumerate()
            .for_each(assoc);
    }
    if moved {
        // Find longest sequence that can remain stationary
        let mut seq = sources
            .longest_increasing_subsequence_by(
                |a, b| a.as_ref().unwrap().0.cmp(&b.as_ref().unwrap().0),
                Option::is_some,
            )
            .into_iter()
            .rev()
            .peekable();
        for (i, (b, source)) in b.into_iter().zip(sources).enumerate().rev() {
            if let Some((_, a)) = source {
                if Some(&i) == seq.peek() {
                    seq.next();
                    cb.unchanged(a, b);
                } else {
                    cb.moved(a, b);
                }
            } else {
                cb.inserted(b);
            }
        }
    } else {
        for (b, source) in b.into_iter().zip(sources).rev() {
            if let Some((_, a)) = source {
                cb.unchanged(a, b);
            } else {
                cb.inserted(b);
            };
        }
    }
}
pub fn diff_by_key < S , T , K : Eq + Hash > ( a : impl IntoIterator < Item = S , IntoIter = impl DoubleEndedIterator < Item = S > > , mut f : impl FnMut ( & S ) -> & K , b : impl IntoIterator < Item = T , IntoIter = impl DoubleEndedIterator < Item = T > > , mut g : impl FnMut ( & T ) -> & K , cb : & mut impl DiffCallback < S , T > , ) { let ( mut a , mut b ) = ( a . into_iter ( ) . de_peekable ( ) , b . into_iter ( ) . de_peekable ( ) ) ; // Sync nodes with same key at start while a . peek ( ) . and_then ( | a | b . peek ( ) . filter ( | & b | f ( a ) == g ( b ) ) ) . is_some ( ) { cb . unchanged ( a . next ( ) . unwrap ( ) , b . next ( ) . unwrap ( ) ) ; } // Sync nodes with same key at end while a . peek_back ( ) . and_then ( | a | b . peek_back ( ) . filter ( | & b | f ( a ) == g ( b ) ) ) . is_some ( ) { cb . unchanged ( a . next_back ( ) . unwrap ( ) , b . next_back ( ) . unwrap ( ) ) ; } if a . peek ( ) . is_none ( ) { return b . rev ( ) . for_each ( | x | cb . inserted ( x ) ) ; // If all of a was synced, add remaining in b } else if b . peek ( ) . is_none ( ) { return a . for_each ( | x | cb . removed ( x ) ) ; // If all of b was synced, remove remaining in a } let ( b , mut sources ) : ( Vec < _ > , Vec < _ > ) = b . map ( | x | ( x , None ) ) . unzip ( ) ; let ( mut last_j , mut moved ) = ( 0 , false ) ; // Associate elements in `a` to their counterparts in `b`, or remove if nonexistant let assoc = | ( i , ( j , a_elem ) ) : ( _ , ( Option < usize > , _ ) ) | { if let Some ( j ) = j { debug_assert ! ( sources [ j ] is_none ( ) " " ) ; // Catch some instances of dupes sources [ j ] = Some ( ( i , a_elem ) ) ; if j < last_j { moved = true ; } last_j = j ; } else { cb . removed ( a_elem ) ; } } ; // If size is small just loop through if b . len ( ) < 4 || a . size_hint ( ) . 1 . map_or ( false , | hi | hi | b . len ( ) < 32 ) { a . map ( | a_elem | ( b . iter ( ) . position ( | b_elem | f ( & a_elem ) == g ( b_elem ) ) , a_elem ) ) . enumerate ( ) . for_each ( assoc ) ; } else { // Map of keys in b to their respective indices let key_index : FxHashMap < _ , _ > = b . iter ( ) . enumerate ( ) . map ( | ( j , ref x ) | ( g ( x ) , j ) ) . collect ( ) ; a . map ( | a_elem | ( key_index . get ( & f ( & a_elem ) ) . copied ( ) , a_elem ) ) . enumerate ( ) . for_each ( assoc ) ; } if moved { // Find longest sequence that can remain stationary let mut seq = sources . longest_increasing_subsequence_by ( | a , b | a . as_ref ( ) . unwrap ( ) . 0 . cmp ( & b . as_ref ( ) . unwrap ( ) . 0 ) , Option :: is_some , ) . into_iter ( ) . rev ( ) . peekable ( ) ; for ( i , ( b , source ) ) in b . into_iter ( ) . zip ( sources ) . enumerate ( ) . rev ( ) { if let Some ( ( _ , a ) ) = source { if Some ( & i ) == seq . peek ( ) { seq . next ( ) ; cb . unchanged ( a , b ) ; } else { cb . moved ( a , b ) ; } } else { cb . inserted ( b ) ; } } } else { for ( b , source ) in b . into_iter ( ) . zip ( sources ) . rev ( ) { if let Some ( ( _ , a ) ) = source { cb . unchanged ( a , b ) ; } else { cb . inserted ( b ) ; } ; } } }
Computes the difference between the two iterators with key extraction functions.
Computes the difference between the two iterators with key extraction functions .
a
impl IntoIterator<Item = S, IntoIter = impl DoubleEndedIterator<Item = S>>
f
impl FnMut(&S) -> &K
b
impl IntoIterator<Item = T, IntoIter = impl DoubleEndedIterator<Item = T>>
g
impl FnMut(&T) -> &K
cb
&mut impl DiffCallback<S, T>
panics
May panic if `a` contains duplicate keys.

[enumerate]: std::iter::Iterator::enumerate
May panic if ` a ` contains duplicate keys . [ enumerate ] : std :: iter :: Iterator :: enumerate