/* ---------------------------------------------------------------------
* Numenta Platform for Intelligent Computing (NuPIC)
* Copyright (C) 2014-2016, Numenta, Inc. Unless you have an agreement
* with Numenta, Inc., for a separate license for this software code, the
* following terms and conditions apply:
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero Public License version 3 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Affero Public License for more details.
*
* You should have received a copy of the GNU Affero Public License
* along with this program. If not, see http://www.gnu.org/licenses.
*
* http://numenta.org/licenses/
* ---------------------------------------------------------------------
*/
package org.numenta.nupic.util;
import java.util.List;
import java.util.function.Function;
import chaschev.lang.Pair;
/**
* <p>
* Allegory to the Python itertools.groupby. Objects of this class
* take a list of inputs and a function to produce keys. The iterator
* or foreach loop generates grouped return values based on the key
* generated by the supplied function.<br>
* For instance:<br>
* <pre>
* Given the list:
* List<Integer> l = Arrays.asList(new Integer[] { 2, 4, 4, 5 });
* and the function:
* Function<Integer, Integer> lambda = x -> x * 3;
*
* A GroupBy can be compose as such:
* GroupBy<Integer, Integer> grouper = GroupBy.of(l, lambda);
*
* ...then iterated over as such:
* for(Pair<Integer, Integer> p : grouper) {
* System.out.println("Pair key: " + p.getKey() + ", pair value: " + p.getValue());
* }
*
* Outputs:
*
* Pair key: 2, pair value: 6
* Pair key: 4, pair value: 12
* Pair key: 4, pair value: 12
* Pair key: 5, pair value: 15
* </p>
* <p>
* Notes: Read up on groupby here:
* https://docs.python.org/dev/library/itertools.html#itertools.groupby
*
* </p>
* @author cogmission
*
* @param <T>
* @param <R>
*/
public class GroupBy<T, R> implements Generator<Pair<T, R>>, PeekableIterator<Pair<T, R>> {
/** serial version */
private static final long serialVersionUID = 1L;
private List<T> iter;
private Function<T, R> fn;
private Generator<Integer> range;
private Pair<T, R> next;
/**
* Constructs a new {@code GroupBy}
*
* @param l the {@link List} containing the items used as input to the
* key generating function.
* @param fn the {@link Function} to be used to generate the keys which describe
* the like contents of each grouping.
*/
public GroupBy(List<T> l, Function<T, R> fn) {
this.iter = l;
this.fn = fn;
this.range = IntGenerator.of(0, iter.size());
if(range.hasNext()) {
T t = iter.get(range.next());
next = new Pair<T, R>(t, fn.apply(t));
}
}
/**
* {@inheritDoc}
*/
@Override
public Pair<T, R> peek() {
return next;
}
/**
* {@inheritDoc}
*/
@Override
public boolean hasNext() {
return next != null;
}
/**
* {@inheritDoc}
*/
@Override
public Pair<T, R> next() {
T t = range.hasNext() ? iter.get(range.next()) : null;
Pair<T, R> ret = next;
next = t != null ? new Pair<T, R>(t, fn.apply(t)) : null;
return ret;
}
/**
* Returns a new {@code GroupBy} composed from the specified list
* and key-generating {@link Function}
*
* @param l the {@link List} containing the items used as input to the
* key generating function.
* @param fn the {@link Function} to be used to generate the keys which describe
* the like contents of each grouping.
* @return
*/
public static <T, R> GroupBy<T, R> of(List<T> l, Function<T, R> fn) {
return new GroupBy<T, R>(l, fn);
}
}