Path: csiph.com!eternal-september.org!feeder.eternal-september.org!reader02.eternal-september.org!.POSTED!not-for-mail From: Daniele Futtorovic Newsgroups: comp.lang.java.programmer Subject: Re: Is there a better way to group and sort objects? Date: Thu, 9 Aug 2018 16:06:59 +0200 Organization: A noiseless patient Spider Lines: 285 Message-ID: References: <8c045ff4-98c5-4253-9136-b26b34cf8f77@googlegroups.com> Mime-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 7bit Injection-Date: Thu, 9 Aug 2018 14:08:13 -0000 (UTC) Injection-Info: reader02.eternal-september.org; posting-host="17451a47b8cd9bd681876d7a42321b59"; logging-data="23053"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX19aS8bxUjDMPJ5zinCukKPy" User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:52.0) Gecko/20100101 Thunderbird/52.9.1 Cancel-Lock: sha1:kHrbd6XpA4EUgiV85PSbM8V6eCQ= In-Reply-To: <8c045ff4-98c5-4253-9136-b26b34cf8f77@googlegroups.com> X-Antivirus-Status: Clean Content-Language: en-GB X-Antivirus: AVG (VPS 180808-4, 08/08/2018), Outbound message Xref: csiph.com comp.lang.java.programmer:38550 On 2018-08-07 15:00, Eric Douglas wrote: > I want to combine and sort objects by key properties and merge their secondary properties. Is there a better way? This is what I came up with.. I was bored, so here's some code. Omitted imports and most of the argument and null checks. Also, you might need Java 9 to run it. public class ScratchPad { private static final class GroupBy { private final String name; private final Function extractor; private final BinaryOperator

accumulatorFunction; private final Supplier

initialValueSupplier; public GroupBy( String name, Function extractor, BinaryOperator

accumulatorFunction, Supplier

initialValueSupplier ) { this.name = name; this.extractor = extractor; this.accumulatorFunction = accumulatorFunction; this.initialValueSupplier = initialValueSupplier; } public GroupBy( String name, Function extractor, BinaryOperator

accumulatorFunction ) { this( name, extractor, accumulatorFunction, null ); } public String name(){ return name; } public Function extractor() { return extractor; } public BinaryOperator

accumulatorFunction() { return accumulatorFunction; } public Accumulator newAccumulator(){ return new Accumulator<>( this, initialValueSupplier == null ? null : initialValueSupplier.get() ); } } private static final class Accumulator { private final GroupBy groupBy; private P accumulation; public Accumulator( GroupBy groupBy ) { this.groupBy = groupBy; } public Accumulator( GroupBy groupBy, P initialValue ) { this( groupBy ); accumulation = initialValue; } public String groupByName(){ return groupBy.name(); } public P accumulation(){ return accumulation; } public P accumulate( T t ){ P newVal = groupBy.extractor().apply(t); return accumulation = accumulation == null ? newVal : groupBy.accumulatorFunction().apply( accumulation, newVal ); } } private static final class GroupAndSort> { private final Function keyExtractor; private final List> merged; private final SortedMap>> sieve = new TreeMap<>() ; @SafeVarargs public static > GroupAndSort newInstance( Class itemClass, Function keyExtractor, GroupBy... merges ){ return new GroupAndSort<>( keyExtractor, List.of( merges ) ); } public GroupAndSort( Function keyExtractor, Collection> merges ){ this.keyExtractor = keyExtractor; List> accs = new ArrayList<>(); accs.add( new GroupBy>( null, List::of, (old, niou) -> { old.addAll( niou ); return old; }, ArrayList::new ) ); Set groupNames = new HashSet<>(); for( GroupBy g: merges ){ if( g.name() == null ){ throw new IllegalArgumentException( "null groupBy name: " + g ); } if( ! groupNames.add(g.name()) ){ throw new IllegalArgumentException( "Duplicate groupBy name: " + g.name() ); } } accs.addAll( merges ); this.merged = List.copyOf( accs ); } public void put( T item ){ K key = keyExtractor.apply( item ); sieve.compute( key, (k, list) -> { if( list == null ){ list = merged.stream().map( GroupBy::newAccumulator ).collect(Collectors.toList()); } list.forEach( acc -> acc.accumulate(item) ); return list; } ); } @SafeVarargs public final void putAll( T... items ){ for( T item: items ) this.put( item ); } public Stream keys(){ return sieve.keySet().stream(); } @SuppressWarnings( "unchecked" ) public Stream items(){ return sieve.values().stream().flatMap( (l) -> ((Collection)l.get(0).accumulation()).stream() ); } @SuppressWarnings( "unchecked" ) public Stream items( K key ){ List> props = sieve.get( key ); if( props == null ){ return Stream.empty(); } else { return ((Collection)props.get(0).accumulation()).stream(); } } public List mergedPropertiesFor( K key ){ List> props = sieve.get( key ); if( props == null ){ return Collections.emptyList(); } else { return props.stream().skip(1).map( Accumulator::accumulation ).collect(Collectors.toList()); } } public Map mergedPropertyMapFor( K key ){ List> props = sieve.get( key ); if( props == null ){ return null; } else { return props.stream().skip(1).collect( Collectors.toMap( Accumulator::groupByName, Accumulator::accumulation ) ); } } } private static final class TestObject { private final String id; private final int sortingKey; private final int intProperty; private final boolean boolProperty; public TestObject( String id, int sortingKey, int intProperty, boolean boolProperty ) { this.id = id; this.sortingKey = sortingKey; this.intProperty = intProperty; this.boolProperty = boolProperty; } public String id(){ return id; } public int sortingKey() { return sortingKey; } public int someIntProperty() { return intProperty; } public boolean someBoolProperty() { return boolProperty; } @Override public String toString() { final StringBuilder sb = new StringBuilder( "TestObject{" ); sb.append( "id='" ).append( id ).append( '\'' ); sb.append( ", sortingKey=" ).append( sortingKey ); sb.append( ", intProperty=" ).append( intProperty ); sb.append( ", boolProperty=" ).append( boolProperty ); sb.append( '}' ); return sb.toString(); } } public static void main( String[] ss ){ TestObject t1 = new TestObject( "t1", 1, 34907, true ); TestObject t2 = new TestObject( "t2", 1, 4, false ); TestObject t3 = new TestObject( "t3", 10, 1, true ); TestObject t4 = new TestObject( "t4", 4, 8, false ); GroupAndSort group = GroupAndSort.newInstance( TestObject.class, TestObject::sortingKey, new GroupBy<>( "intProperty", TestObject::someIntProperty, Integer::sum ), new GroupBy<>( "boolProperty", TestObject::someBoolProperty, Boolean::logicalAnd ) ); group.putAll( t1, t3, t4, t2 ); System.err.println( "Items: " + group.items().collect(Collectors.toList()) ); System.err.println( "Keys: " + group.keys().collect(Collectors.toList()) ); System.err.println( "Grouped items:" ); group.keys().forEach( key -> System.err.printf( "%s -> %s%n", key, group.items(key).collect(Collectors.toList()) ) ); System.err.println( "Merged properties:" ); group.keys().forEach( key -> System.err.printf( "%s -> %s%n", key, group.mergedPropertiesFor( key ) ) ); System.err.println( "Merged property maps:" ); group.keys().forEach( key -> System.err.printf( "%s -> %s%n", key, group.mergedPropertyMapFor( key ) ) ); } }