Nesting Values with Java 8 stream and collector










-1















I have following data set to represent sales record:



 sn| Channel | Category | Brand |qty | gross | 
1 |"Mini Market" | "Large MM" | "ARIEL" |3 | 100 |
2 |"Mini Market" | "Large MM" | "ARIEL" |6 | 200|
3 |"Mini Market" | "Large MM" | "GILLETTE" |12 | 103.88|
4 |"Mini Market" | "Large MM" | "OLAY" |2 | 50 |
5 |"Mini Market" | "Large MM" | "OLAY" |6 | 10|
6 |"Mini Market" | "Small MM" | "GILLETTE" |5 | 20 |
7 |"Mini Market" | "Small MM" | "GILLETTE" |3 | 30|
8 |"Mini Market" | "Small MM" | "OLAY" |3 | 80.3 |
9 |"Mini Market" | "Small MM" | "ORAL B" |6 | 100 |
10|"Mini Market" | "Small MM" | "ORAL B" |7 | 150 |


POJO Classes:



class SalesRecord
private String channel;
private String category;
private String brand;
private int qty;
private double gross;

//getters and setters




class PivotTable
Map<Integer,Set<String>> uniqueAttirbuteMap;
List<Pivot> pivot;
//other fields and methods



class Pivot
public String attribute;
Map<String, Double> aggregates;
List<Pivot> pivotList;
//other fields and methods




Custom Collector class:



public class SalesCollector implements Collector<SalesRecord,
SalesCollector.Accumulator, PivotTable>
private static final String GROSS_SUM = "sum_gross";
private static final String QTY_SUM = "sum_qty"

public List<Double> prices = Lists.newArrayList();
public List<Double> qtys = Lists.newArrayList();

public double totalSales = 0;
public double totalQty = 0;

public SalesCollector(final List<GroupBy> groups)
this.groups = groups;



@Override
public Supplier<Accumulator> supplier()
return () -> new Accumulator(this.groups, this);


@Override
public BiConsumer<Accumulator, SalesRecord> accumulator()
return Accumulator::accumulate;


@Override
public BinaryOperator<Accumulator> combiner()
return Accumulator::combine;


@Override
public Function<Accumulator, PivotTable> finisher()
return Accumulator::toSummary;


static class Accumulator

private final List<GroupBy> groups;
private final SalesCollector collector;
PivotTable pivotTable = new PivotTable();

Accumulator(final List<GroupBy> groups, final SalesCollector collector)
this.groups = groups;
this.collector = collector;


void accumulate(SalesRecord elem)
double sum = pivotTable.getAggregates().getOrDefault(GROSS_SUM, 0D) + elem.getGross();
pivotTable.getAggregates().put(GROSS_SUM, sum);

double qtySum = pivotTable.getAggregates().getOrDefault(QTY_SUM, 0D) + elem.getQty();
pivotTable.getAggregates().put(QTY_SUM, qtySum);


Accumulator combine(Accumulator other)

final PivotTable summary = other.toSummary();

double sum_qty =
pivotTable.getAggregates().get(QTY_SUM) + summary.getAggregates().get(QTY_SUM);
pivotTable.getAggregates().put(QTY_SUM, sum_qty);

double sum_gross =
pivotTable.getAggregates().get(GROSS_SUM) + summary.getAggregates().get(GROSS_SUM);
pivotTable.getAggregates().put(GROSS_SUM, sum_gross);

return this;


PivotTable toSummary()
double sumGross = pivotTable.getAggregates().get(GROSS_SUM);
collector.prices.add(sumGross);
collector.totalSales += sumGross;


double sumQty = pivotTable.getAggregates().get(QTY_SUM);
collector.qtys.add(sumQty);
collector.totalQty += sumQty;

return pivotTable;





Currently I am using stream and gropuing and have a custom collector to calculate values for qty and gross as follows:



SalesCollector collector = new SalesCollector(groups);
final Map<String, Map<String, Map<String, PivotTable>>> results = salesRecords.stream()
.collect(groupingBy(SalesRecord::getChannel(), TreeMap::new,
groupingBy(SalesRecord::getCategoryName(), TreeMap::new,
groupingBy(SalesRecord::getBrand(), TreeMap::new, collector))));

List<PivotTable> myList = results.values().stream()
.map(Map::values)
.flatMap(Collection::stream)
.map(Map::values)
.flatMap(Collection::stream)
.collect(Collectors.toList());


My current result is as follows:



PivotTable(pivot:[Pivot(attribute:Mini Market), Pivot(attribute:Large MM), Pivot(attribute:ARIEL, aggregates: sum_qty=9, sum_gross=300 )])
PivotTable(pivot:[Pivot(attribute:Mini Market), Pivot(attribute:Large MM), Pivot(attribute:GILLETTE, aggregates: sum_qty = 12, sum_gross= 103.88)])
PivotTable(pivot:[Pivot(attribute:Mini Market), Pivot(attribute:Large MM), Pivot(attribute:OLAY, aggregates: sum_qty = 8, sum_gross= 60)])
PivotTable(pivot:[Pivot(attribute:Mini Market), Pivot(attribute:Small MM), Pivot(attribute:OLAY, aggregates: sum_qty = 3, sum_gross= 80.3)])
PivotTable(pivot:[Pivot(attribute:Mini Market), Pivot(attribute:Small MM), Pivot(attribute:GILLETTE, aggregates: sum_qty = 8, sum_gross= 50)])
PivotTable(pivot:[Pivot(attribute:Mini Market), Pivot(attribute:Small MM), Pivot(attribute:ORAL B, aggregates: sum_qty = 13, sum_gross= 250)])


What I'm trying to achieve is as follows:



PivotTable(pivot:[Pivot(attribute:Mini Market), Pivot(attribute:Large MM), pivotList:[Pivot(attribute:ARIEL, aggregates: sum_qty=9, sum_gross=300 ),
Pivot(attribute:GILLETTE, aggregates: sum_qty = 12, sum_gross= 103.88),Pivot(attribute:OLAY, aggregates: sum_qty = 8, sum_gross= 60)])
PivotTable(pivot:[Pivot(attribute:Mini Market), Pivot(attribute:Small MM), Pivot(attribute:GILLETTE, aggregates: sum_qty = 8, sum_gross= 50)]),
Pivot(attribute:OLAY, aggregates: sum_qty = 3, sum_gross= 80.3),Pivot(attribute:ORAL B, aggregates: sum_qty = 13, sum_gross= 250)])


To put it simply, I want nesting as follows:



Mini Market:
Large MM:
ARIEL: sum_qty = 9, sum_gross= 300
GILLETTE: sum_qty = 12, sum_gross= 103.88
OLAY: sum_qty = 8, sum_gross= 60

Mini Market:
Small MM:
GILLETTE: sum_qty = 8, sum_gross= 50
OLAY: sum_qty = 3, sum_gross= 80.3
ORAL B: sum_qty = 13, sum_gross= 250


Is it possible to achive this with current collector result itself with same grouping? What would be best way to achieve this?










share|improve this question
























  • Could you share the PivotTable and SalesRecord POJOs? Also, what is the data type of the result that you are expecting now?

    – nullpointer
    Nov 14 '18 at 4:19











  • @nullpointer I've added the POJOs. The data type of the result expected id list<PivotTable>

    – Sujal
    Nov 14 '18 at 5:01















-1















I have following data set to represent sales record:



 sn| Channel | Category | Brand |qty | gross | 
1 |"Mini Market" | "Large MM" | "ARIEL" |3 | 100 |
2 |"Mini Market" | "Large MM" | "ARIEL" |6 | 200|
3 |"Mini Market" | "Large MM" | "GILLETTE" |12 | 103.88|
4 |"Mini Market" | "Large MM" | "OLAY" |2 | 50 |
5 |"Mini Market" | "Large MM" | "OLAY" |6 | 10|
6 |"Mini Market" | "Small MM" | "GILLETTE" |5 | 20 |
7 |"Mini Market" | "Small MM" | "GILLETTE" |3 | 30|
8 |"Mini Market" | "Small MM" | "OLAY" |3 | 80.3 |
9 |"Mini Market" | "Small MM" | "ORAL B" |6 | 100 |
10|"Mini Market" | "Small MM" | "ORAL B" |7 | 150 |


POJO Classes:



class SalesRecord
private String channel;
private String category;
private String brand;
private int qty;
private double gross;

//getters and setters




class PivotTable
Map<Integer,Set<String>> uniqueAttirbuteMap;
List<Pivot> pivot;
//other fields and methods



class Pivot
public String attribute;
Map<String, Double> aggregates;
List<Pivot> pivotList;
//other fields and methods




Custom Collector class:



public class SalesCollector implements Collector<SalesRecord,
SalesCollector.Accumulator, PivotTable>
private static final String GROSS_SUM = "sum_gross";
private static final String QTY_SUM = "sum_qty"

public List<Double> prices = Lists.newArrayList();
public List<Double> qtys = Lists.newArrayList();

public double totalSales = 0;
public double totalQty = 0;

public SalesCollector(final List<GroupBy> groups)
this.groups = groups;



@Override
public Supplier<Accumulator> supplier()
return () -> new Accumulator(this.groups, this);


@Override
public BiConsumer<Accumulator, SalesRecord> accumulator()
return Accumulator::accumulate;


@Override
public BinaryOperator<Accumulator> combiner()
return Accumulator::combine;


@Override
public Function<Accumulator, PivotTable> finisher()
return Accumulator::toSummary;


static class Accumulator

private final List<GroupBy> groups;
private final SalesCollector collector;
PivotTable pivotTable = new PivotTable();

Accumulator(final List<GroupBy> groups, final SalesCollector collector)
this.groups = groups;
this.collector = collector;


void accumulate(SalesRecord elem)
double sum = pivotTable.getAggregates().getOrDefault(GROSS_SUM, 0D) + elem.getGross();
pivotTable.getAggregates().put(GROSS_SUM, sum);

double qtySum = pivotTable.getAggregates().getOrDefault(QTY_SUM, 0D) + elem.getQty();
pivotTable.getAggregates().put(QTY_SUM, qtySum);


Accumulator combine(Accumulator other)

final PivotTable summary = other.toSummary();

double sum_qty =
pivotTable.getAggregates().get(QTY_SUM) + summary.getAggregates().get(QTY_SUM);
pivotTable.getAggregates().put(QTY_SUM, sum_qty);

double sum_gross =
pivotTable.getAggregates().get(GROSS_SUM) + summary.getAggregates().get(GROSS_SUM);
pivotTable.getAggregates().put(GROSS_SUM, sum_gross);

return this;


PivotTable toSummary()
double sumGross = pivotTable.getAggregates().get(GROSS_SUM);
collector.prices.add(sumGross);
collector.totalSales += sumGross;


double sumQty = pivotTable.getAggregates().get(QTY_SUM);
collector.qtys.add(sumQty);
collector.totalQty += sumQty;

return pivotTable;





Currently I am using stream and gropuing and have a custom collector to calculate values for qty and gross as follows:



SalesCollector collector = new SalesCollector(groups);
final Map<String, Map<String, Map<String, PivotTable>>> results = salesRecords.stream()
.collect(groupingBy(SalesRecord::getChannel(), TreeMap::new,
groupingBy(SalesRecord::getCategoryName(), TreeMap::new,
groupingBy(SalesRecord::getBrand(), TreeMap::new, collector))));

List<PivotTable> myList = results.values().stream()
.map(Map::values)
.flatMap(Collection::stream)
.map(Map::values)
.flatMap(Collection::stream)
.collect(Collectors.toList());


My current result is as follows:



PivotTable(pivot:[Pivot(attribute:Mini Market), Pivot(attribute:Large MM), Pivot(attribute:ARIEL, aggregates: sum_qty=9, sum_gross=300 )])
PivotTable(pivot:[Pivot(attribute:Mini Market), Pivot(attribute:Large MM), Pivot(attribute:GILLETTE, aggregates: sum_qty = 12, sum_gross= 103.88)])
PivotTable(pivot:[Pivot(attribute:Mini Market), Pivot(attribute:Large MM), Pivot(attribute:OLAY, aggregates: sum_qty = 8, sum_gross= 60)])
PivotTable(pivot:[Pivot(attribute:Mini Market), Pivot(attribute:Small MM), Pivot(attribute:OLAY, aggregates: sum_qty = 3, sum_gross= 80.3)])
PivotTable(pivot:[Pivot(attribute:Mini Market), Pivot(attribute:Small MM), Pivot(attribute:GILLETTE, aggregates: sum_qty = 8, sum_gross= 50)])
PivotTable(pivot:[Pivot(attribute:Mini Market), Pivot(attribute:Small MM), Pivot(attribute:ORAL B, aggregates: sum_qty = 13, sum_gross= 250)])


What I'm trying to achieve is as follows:



PivotTable(pivot:[Pivot(attribute:Mini Market), Pivot(attribute:Large MM), pivotList:[Pivot(attribute:ARIEL, aggregates: sum_qty=9, sum_gross=300 ),
Pivot(attribute:GILLETTE, aggregates: sum_qty = 12, sum_gross= 103.88),Pivot(attribute:OLAY, aggregates: sum_qty = 8, sum_gross= 60)])
PivotTable(pivot:[Pivot(attribute:Mini Market), Pivot(attribute:Small MM), Pivot(attribute:GILLETTE, aggregates: sum_qty = 8, sum_gross= 50)]),
Pivot(attribute:OLAY, aggregates: sum_qty = 3, sum_gross= 80.3),Pivot(attribute:ORAL B, aggregates: sum_qty = 13, sum_gross= 250)])


To put it simply, I want nesting as follows:



Mini Market:
Large MM:
ARIEL: sum_qty = 9, sum_gross= 300
GILLETTE: sum_qty = 12, sum_gross= 103.88
OLAY: sum_qty = 8, sum_gross= 60

Mini Market:
Small MM:
GILLETTE: sum_qty = 8, sum_gross= 50
OLAY: sum_qty = 3, sum_gross= 80.3
ORAL B: sum_qty = 13, sum_gross= 250


Is it possible to achive this with current collector result itself with same grouping? What would be best way to achieve this?










share|improve this question
























  • Could you share the PivotTable and SalesRecord POJOs? Also, what is the data type of the result that you are expecting now?

    – nullpointer
    Nov 14 '18 at 4:19











  • @nullpointer I've added the POJOs. The data type of the result expected id list<PivotTable>

    – Sujal
    Nov 14 '18 at 5:01













-1












-1








-1








I have following data set to represent sales record:



 sn| Channel | Category | Brand |qty | gross | 
1 |"Mini Market" | "Large MM" | "ARIEL" |3 | 100 |
2 |"Mini Market" | "Large MM" | "ARIEL" |6 | 200|
3 |"Mini Market" | "Large MM" | "GILLETTE" |12 | 103.88|
4 |"Mini Market" | "Large MM" | "OLAY" |2 | 50 |
5 |"Mini Market" | "Large MM" | "OLAY" |6 | 10|
6 |"Mini Market" | "Small MM" | "GILLETTE" |5 | 20 |
7 |"Mini Market" | "Small MM" | "GILLETTE" |3 | 30|
8 |"Mini Market" | "Small MM" | "OLAY" |3 | 80.3 |
9 |"Mini Market" | "Small MM" | "ORAL B" |6 | 100 |
10|"Mini Market" | "Small MM" | "ORAL B" |7 | 150 |


POJO Classes:



class SalesRecord
private String channel;
private String category;
private String brand;
private int qty;
private double gross;

//getters and setters




class PivotTable
Map<Integer,Set<String>> uniqueAttirbuteMap;
List<Pivot> pivot;
//other fields and methods



class Pivot
public String attribute;
Map<String, Double> aggregates;
List<Pivot> pivotList;
//other fields and methods




Custom Collector class:



public class SalesCollector implements Collector<SalesRecord,
SalesCollector.Accumulator, PivotTable>
private static final String GROSS_SUM = "sum_gross";
private static final String QTY_SUM = "sum_qty"

public List<Double> prices = Lists.newArrayList();
public List<Double> qtys = Lists.newArrayList();

public double totalSales = 0;
public double totalQty = 0;

public SalesCollector(final List<GroupBy> groups)
this.groups = groups;



@Override
public Supplier<Accumulator> supplier()
return () -> new Accumulator(this.groups, this);


@Override
public BiConsumer<Accumulator, SalesRecord> accumulator()
return Accumulator::accumulate;


@Override
public BinaryOperator<Accumulator> combiner()
return Accumulator::combine;


@Override
public Function<Accumulator, PivotTable> finisher()
return Accumulator::toSummary;


static class Accumulator

private final List<GroupBy> groups;
private final SalesCollector collector;
PivotTable pivotTable = new PivotTable();

Accumulator(final List<GroupBy> groups, final SalesCollector collector)
this.groups = groups;
this.collector = collector;


void accumulate(SalesRecord elem)
double sum = pivotTable.getAggregates().getOrDefault(GROSS_SUM, 0D) + elem.getGross();
pivotTable.getAggregates().put(GROSS_SUM, sum);

double qtySum = pivotTable.getAggregates().getOrDefault(QTY_SUM, 0D) + elem.getQty();
pivotTable.getAggregates().put(QTY_SUM, qtySum);


Accumulator combine(Accumulator other)

final PivotTable summary = other.toSummary();

double sum_qty =
pivotTable.getAggregates().get(QTY_SUM) + summary.getAggregates().get(QTY_SUM);
pivotTable.getAggregates().put(QTY_SUM, sum_qty);

double sum_gross =
pivotTable.getAggregates().get(GROSS_SUM) + summary.getAggregates().get(GROSS_SUM);
pivotTable.getAggregates().put(GROSS_SUM, sum_gross);

return this;


PivotTable toSummary()
double sumGross = pivotTable.getAggregates().get(GROSS_SUM);
collector.prices.add(sumGross);
collector.totalSales += sumGross;


double sumQty = pivotTable.getAggregates().get(QTY_SUM);
collector.qtys.add(sumQty);
collector.totalQty += sumQty;

return pivotTable;





Currently I am using stream and gropuing and have a custom collector to calculate values for qty and gross as follows:



SalesCollector collector = new SalesCollector(groups);
final Map<String, Map<String, Map<String, PivotTable>>> results = salesRecords.stream()
.collect(groupingBy(SalesRecord::getChannel(), TreeMap::new,
groupingBy(SalesRecord::getCategoryName(), TreeMap::new,
groupingBy(SalesRecord::getBrand(), TreeMap::new, collector))));

List<PivotTable> myList = results.values().stream()
.map(Map::values)
.flatMap(Collection::stream)
.map(Map::values)
.flatMap(Collection::stream)
.collect(Collectors.toList());


My current result is as follows:



PivotTable(pivot:[Pivot(attribute:Mini Market), Pivot(attribute:Large MM), Pivot(attribute:ARIEL, aggregates: sum_qty=9, sum_gross=300 )])
PivotTable(pivot:[Pivot(attribute:Mini Market), Pivot(attribute:Large MM), Pivot(attribute:GILLETTE, aggregates: sum_qty = 12, sum_gross= 103.88)])
PivotTable(pivot:[Pivot(attribute:Mini Market), Pivot(attribute:Large MM), Pivot(attribute:OLAY, aggregates: sum_qty = 8, sum_gross= 60)])
PivotTable(pivot:[Pivot(attribute:Mini Market), Pivot(attribute:Small MM), Pivot(attribute:OLAY, aggregates: sum_qty = 3, sum_gross= 80.3)])
PivotTable(pivot:[Pivot(attribute:Mini Market), Pivot(attribute:Small MM), Pivot(attribute:GILLETTE, aggregates: sum_qty = 8, sum_gross= 50)])
PivotTable(pivot:[Pivot(attribute:Mini Market), Pivot(attribute:Small MM), Pivot(attribute:ORAL B, aggregates: sum_qty = 13, sum_gross= 250)])


What I'm trying to achieve is as follows:



PivotTable(pivot:[Pivot(attribute:Mini Market), Pivot(attribute:Large MM), pivotList:[Pivot(attribute:ARIEL, aggregates: sum_qty=9, sum_gross=300 ),
Pivot(attribute:GILLETTE, aggregates: sum_qty = 12, sum_gross= 103.88),Pivot(attribute:OLAY, aggregates: sum_qty = 8, sum_gross= 60)])
PivotTable(pivot:[Pivot(attribute:Mini Market), Pivot(attribute:Small MM), Pivot(attribute:GILLETTE, aggregates: sum_qty = 8, sum_gross= 50)]),
Pivot(attribute:OLAY, aggregates: sum_qty = 3, sum_gross= 80.3),Pivot(attribute:ORAL B, aggregates: sum_qty = 13, sum_gross= 250)])


To put it simply, I want nesting as follows:



Mini Market:
Large MM:
ARIEL: sum_qty = 9, sum_gross= 300
GILLETTE: sum_qty = 12, sum_gross= 103.88
OLAY: sum_qty = 8, sum_gross= 60

Mini Market:
Small MM:
GILLETTE: sum_qty = 8, sum_gross= 50
OLAY: sum_qty = 3, sum_gross= 80.3
ORAL B: sum_qty = 13, sum_gross= 250


Is it possible to achive this with current collector result itself with same grouping? What would be best way to achieve this?










share|improve this question
















I have following data set to represent sales record:



 sn| Channel | Category | Brand |qty | gross | 
1 |"Mini Market" | "Large MM" | "ARIEL" |3 | 100 |
2 |"Mini Market" | "Large MM" | "ARIEL" |6 | 200|
3 |"Mini Market" | "Large MM" | "GILLETTE" |12 | 103.88|
4 |"Mini Market" | "Large MM" | "OLAY" |2 | 50 |
5 |"Mini Market" | "Large MM" | "OLAY" |6 | 10|
6 |"Mini Market" | "Small MM" | "GILLETTE" |5 | 20 |
7 |"Mini Market" | "Small MM" | "GILLETTE" |3 | 30|
8 |"Mini Market" | "Small MM" | "OLAY" |3 | 80.3 |
9 |"Mini Market" | "Small MM" | "ORAL B" |6 | 100 |
10|"Mini Market" | "Small MM" | "ORAL B" |7 | 150 |


POJO Classes:



class SalesRecord
private String channel;
private String category;
private String brand;
private int qty;
private double gross;

//getters and setters




class PivotTable
Map<Integer,Set<String>> uniqueAttirbuteMap;
List<Pivot> pivot;
//other fields and methods



class Pivot
public String attribute;
Map<String, Double> aggregates;
List<Pivot> pivotList;
//other fields and methods




Custom Collector class:



public class SalesCollector implements Collector<SalesRecord,
SalesCollector.Accumulator, PivotTable>
private static final String GROSS_SUM = "sum_gross";
private static final String QTY_SUM = "sum_qty"

public List<Double> prices = Lists.newArrayList();
public List<Double> qtys = Lists.newArrayList();

public double totalSales = 0;
public double totalQty = 0;

public SalesCollector(final List<GroupBy> groups)
this.groups = groups;



@Override
public Supplier<Accumulator> supplier()
return () -> new Accumulator(this.groups, this);


@Override
public BiConsumer<Accumulator, SalesRecord> accumulator()
return Accumulator::accumulate;


@Override
public BinaryOperator<Accumulator> combiner()
return Accumulator::combine;


@Override
public Function<Accumulator, PivotTable> finisher()
return Accumulator::toSummary;


static class Accumulator

private final List<GroupBy> groups;
private final SalesCollector collector;
PivotTable pivotTable = new PivotTable();

Accumulator(final List<GroupBy> groups, final SalesCollector collector)
this.groups = groups;
this.collector = collector;


void accumulate(SalesRecord elem)
double sum = pivotTable.getAggregates().getOrDefault(GROSS_SUM, 0D) + elem.getGross();
pivotTable.getAggregates().put(GROSS_SUM, sum);

double qtySum = pivotTable.getAggregates().getOrDefault(QTY_SUM, 0D) + elem.getQty();
pivotTable.getAggregates().put(QTY_SUM, qtySum);


Accumulator combine(Accumulator other)

final PivotTable summary = other.toSummary();

double sum_qty =
pivotTable.getAggregates().get(QTY_SUM) + summary.getAggregates().get(QTY_SUM);
pivotTable.getAggregates().put(QTY_SUM, sum_qty);

double sum_gross =
pivotTable.getAggregates().get(GROSS_SUM) + summary.getAggregates().get(GROSS_SUM);
pivotTable.getAggregates().put(GROSS_SUM, sum_gross);

return this;


PivotTable toSummary()
double sumGross = pivotTable.getAggregates().get(GROSS_SUM);
collector.prices.add(sumGross);
collector.totalSales += sumGross;


double sumQty = pivotTable.getAggregates().get(QTY_SUM);
collector.qtys.add(sumQty);
collector.totalQty += sumQty;

return pivotTable;





Currently I am using stream and gropuing and have a custom collector to calculate values for qty and gross as follows:



SalesCollector collector = new SalesCollector(groups);
final Map<String, Map<String, Map<String, PivotTable>>> results = salesRecords.stream()
.collect(groupingBy(SalesRecord::getChannel(), TreeMap::new,
groupingBy(SalesRecord::getCategoryName(), TreeMap::new,
groupingBy(SalesRecord::getBrand(), TreeMap::new, collector))));

List<PivotTable> myList = results.values().stream()
.map(Map::values)
.flatMap(Collection::stream)
.map(Map::values)
.flatMap(Collection::stream)
.collect(Collectors.toList());


My current result is as follows:



PivotTable(pivot:[Pivot(attribute:Mini Market), Pivot(attribute:Large MM), Pivot(attribute:ARIEL, aggregates: sum_qty=9, sum_gross=300 )])
PivotTable(pivot:[Pivot(attribute:Mini Market), Pivot(attribute:Large MM), Pivot(attribute:GILLETTE, aggregates: sum_qty = 12, sum_gross= 103.88)])
PivotTable(pivot:[Pivot(attribute:Mini Market), Pivot(attribute:Large MM), Pivot(attribute:OLAY, aggregates: sum_qty = 8, sum_gross= 60)])
PivotTable(pivot:[Pivot(attribute:Mini Market), Pivot(attribute:Small MM), Pivot(attribute:OLAY, aggregates: sum_qty = 3, sum_gross= 80.3)])
PivotTable(pivot:[Pivot(attribute:Mini Market), Pivot(attribute:Small MM), Pivot(attribute:GILLETTE, aggregates: sum_qty = 8, sum_gross= 50)])
PivotTable(pivot:[Pivot(attribute:Mini Market), Pivot(attribute:Small MM), Pivot(attribute:ORAL B, aggregates: sum_qty = 13, sum_gross= 250)])


What I'm trying to achieve is as follows:



PivotTable(pivot:[Pivot(attribute:Mini Market), Pivot(attribute:Large MM), pivotList:[Pivot(attribute:ARIEL, aggregates: sum_qty=9, sum_gross=300 ),
Pivot(attribute:GILLETTE, aggregates: sum_qty = 12, sum_gross= 103.88),Pivot(attribute:OLAY, aggregates: sum_qty = 8, sum_gross= 60)])
PivotTable(pivot:[Pivot(attribute:Mini Market), Pivot(attribute:Small MM), Pivot(attribute:GILLETTE, aggregates: sum_qty = 8, sum_gross= 50)]),
Pivot(attribute:OLAY, aggregates: sum_qty = 3, sum_gross= 80.3),Pivot(attribute:ORAL B, aggregates: sum_qty = 13, sum_gross= 250)])


To put it simply, I want nesting as follows:



Mini Market:
Large MM:
ARIEL: sum_qty = 9, sum_gross= 300
GILLETTE: sum_qty = 12, sum_gross= 103.88
OLAY: sum_qty = 8, sum_gross= 60

Mini Market:
Small MM:
GILLETTE: sum_qty = 8, sum_gross= 50
OLAY: sum_qty = 3, sum_gross= 80.3
ORAL B: sum_qty = 13, sum_gross= 250


Is it possible to achive this with current collector result itself with same grouping? What would be best way to achieve this?







java java-stream collectors






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 14 '18 at 10:18







Sujal

















asked Nov 14 '18 at 4:03









SujalSujal

3291925




3291925












  • Could you share the PivotTable and SalesRecord POJOs? Also, what is the data type of the result that you are expecting now?

    – nullpointer
    Nov 14 '18 at 4:19











  • @nullpointer I've added the POJOs. The data type of the result expected id list<PivotTable>

    – Sujal
    Nov 14 '18 at 5:01

















  • Could you share the PivotTable and SalesRecord POJOs? Also, what is the data type of the result that you are expecting now?

    – nullpointer
    Nov 14 '18 at 4:19











  • @nullpointer I've added the POJOs. The data type of the result expected id list<PivotTable>

    – Sujal
    Nov 14 '18 at 5:01
















Could you share the PivotTable and SalesRecord POJOs? Also, what is the data type of the result that you are expecting now?

– nullpointer
Nov 14 '18 at 4:19





Could you share the PivotTable and SalesRecord POJOs? Also, what is the data type of the result that you are expecting now?

– nullpointer
Nov 14 '18 at 4:19













@nullpointer I've added the POJOs. The data type of the result expected id list<PivotTable>

– Sujal
Nov 14 '18 at 5:01





@nullpointer I've added the POJOs. The data type of the result expected id list<PivotTable>

– Sujal
Nov 14 '18 at 5:01












1 Answer
1






active

oldest

votes


















1














I extended your stream by the missing collector. It's a dedicated Collector which maps each SalesRecord to a Map<String, Double>. Since Map has one value type I decided to use Double.



 Supplier<Map<String, Double>> supplier = TreeMap::new;
BiConsumer<Map<String, Double>, SalesRecord> biConsumer = (map, sr) ->
map.merge("sum_qty", Double.valueOf(sr.getQty()), (qtySum, qty) -> qtySum + qty);
map.merge("sum_gross", sr.getGross(), (grossSum, gross) -> grossSum + gross);
;
BinaryOperator<Map<String, Double>> binaryOperator = (l, r) ->
l.compute("sum_qty", (k, v) -> v + r.get("sum_qty"));
l.compute("sum_gross", (k, v) -> v + r.get("sum_gross"));
return l;
;
Collector<SalesRecord, Map<String, Double>, Map<String, Double>> collector = Collector.of(supplier, biConsumer, binaryOperator);


Now addded as the last downstream collector to the stream:



Map<String, Map<String, Map<String, Map<String, Double>>>> grouped = salesRecords.stream().collect(Collectors.groupingBy(
SalesRecord::getChannel, TreeMap::new,
Collectors.groupingBy(SalesRecord::getCategory, TreeMap::new,
Collectors.groupingBy(SalesRecord::getBrand, collector))));


The result is a Map<String, Map<String, Map<String, Map<String, Double>>>> grouped and looks like that (added line breaks for readability):



Mini Market= 
Large MM=
OLAY=sum_gross=60.0, sum_qty=8.0,
ARIEL=sum_gross=300.0, sum_qty=9.0,
GILLETTE=sum_gross=103.88, sum_qty=12.0,
Small MM=
OLAY=sum_gross=80.3, sum_qty=3.0,
ORAL B=sum_gross=250.0, sum_qty=13.0,
GILLETTE=sum_gross=50.0, sum_qty=8.0







share|improve this answer























  • Thanks for the answer, I've edited the question and added the missing collector class.

    – Sujal
    Nov 14 '18 at 10:19











  • As far as I see SalesCollector / Accumulator build the sums. Why did you seaprate the code between SalesCollector and Accumulator? They are tightly coupled.

    – LuCio
    Nov 14 '18 at 10:57











  • You asked is it "possible to achive this with current collector result itself with same grouping?" I did it. And: "What would be best way to achieve this?" I don't want to say, my answer shows the best way but I think the code shown has less overhead than SalesCollector / Accumulator.

    – LuCio
    Nov 14 '18 at 11:01












  • So my question now is how do I get the output as List<Pivot> instead and not Map<String, Map<String, Map<String, Map<String, Double>>>> where 'attribute' of Pivot being value of channel/category/brand and aggregates being sum values?

    – Sujal
    Nov 14 '18 at 16:35











  • I'm not sure if I understand your question. The variable grouped corresponds to the field aggregates of Pivot. But I don't know what the purpose of Pivot.attribute is.

    – LuCio
    Nov 14 '18 at 17:20










Your Answer






StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");

StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "1"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);

else
createEditor();

);

function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);



);













draft saved

draft discarded


















StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53293034%2fnesting-values-with-java-8-stream-and-collector%23new-answer', 'question_page');

);

Post as a guest















Required, but never shown

























1 Answer
1






active

oldest

votes








1 Answer
1






active

oldest

votes









active

oldest

votes






active

oldest

votes









1














I extended your stream by the missing collector. It's a dedicated Collector which maps each SalesRecord to a Map<String, Double>. Since Map has one value type I decided to use Double.



 Supplier<Map<String, Double>> supplier = TreeMap::new;
BiConsumer<Map<String, Double>, SalesRecord> biConsumer = (map, sr) ->
map.merge("sum_qty", Double.valueOf(sr.getQty()), (qtySum, qty) -> qtySum + qty);
map.merge("sum_gross", sr.getGross(), (grossSum, gross) -> grossSum + gross);
;
BinaryOperator<Map<String, Double>> binaryOperator = (l, r) ->
l.compute("sum_qty", (k, v) -> v + r.get("sum_qty"));
l.compute("sum_gross", (k, v) -> v + r.get("sum_gross"));
return l;
;
Collector<SalesRecord, Map<String, Double>, Map<String, Double>> collector = Collector.of(supplier, biConsumer, binaryOperator);


Now addded as the last downstream collector to the stream:



Map<String, Map<String, Map<String, Map<String, Double>>>> grouped = salesRecords.stream().collect(Collectors.groupingBy(
SalesRecord::getChannel, TreeMap::new,
Collectors.groupingBy(SalesRecord::getCategory, TreeMap::new,
Collectors.groupingBy(SalesRecord::getBrand, collector))));


The result is a Map<String, Map<String, Map<String, Map<String, Double>>>> grouped and looks like that (added line breaks for readability):



Mini Market= 
Large MM=
OLAY=sum_gross=60.0, sum_qty=8.0,
ARIEL=sum_gross=300.0, sum_qty=9.0,
GILLETTE=sum_gross=103.88, sum_qty=12.0,
Small MM=
OLAY=sum_gross=80.3, sum_qty=3.0,
ORAL B=sum_gross=250.0, sum_qty=13.0,
GILLETTE=sum_gross=50.0, sum_qty=8.0







share|improve this answer























  • Thanks for the answer, I've edited the question and added the missing collector class.

    – Sujal
    Nov 14 '18 at 10:19











  • As far as I see SalesCollector / Accumulator build the sums. Why did you seaprate the code between SalesCollector and Accumulator? They are tightly coupled.

    – LuCio
    Nov 14 '18 at 10:57











  • You asked is it "possible to achive this with current collector result itself with same grouping?" I did it. And: "What would be best way to achieve this?" I don't want to say, my answer shows the best way but I think the code shown has less overhead than SalesCollector / Accumulator.

    – LuCio
    Nov 14 '18 at 11:01












  • So my question now is how do I get the output as List<Pivot> instead and not Map<String, Map<String, Map<String, Map<String, Double>>>> where 'attribute' of Pivot being value of channel/category/brand and aggregates being sum values?

    – Sujal
    Nov 14 '18 at 16:35











  • I'm not sure if I understand your question. The variable grouped corresponds to the field aggregates of Pivot. But I don't know what the purpose of Pivot.attribute is.

    – LuCio
    Nov 14 '18 at 17:20















1














I extended your stream by the missing collector. It's a dedicated Collector which maps each SalesRecord to a Map<String, Double>. Since Map has one value type I decided to use Double.



 Supplier<Map<String, Double>> supplier = TreeMap::new;
BiConsumer<Map<String, Double>, SalesRecord> biConsumer = (map, sr) ->
map.merge("sum_qty", Double.valueOf(sr.getQty()), (qtySum, qty) -> qtySum + qty);
map.merge("sum_gross", sr.getGross(), (grossSum, gross) -> grossSum + gross);
;
BinaryOperator<Map<String, Double>> binaryOperator = (l, r) ->
l.compute("sum_qty", (k, v) -> v + r.get("sum_qty"));
l.compute("sum_gross", (k, v) -> v + r.get("sum_gross"));
return l;
;
Collector<SalesRecord, Map<String, Double>, Map<String, Double>> collector = Collector.of(supplier, biConsumer, binaryOperator);


Now addded as the last downstream collector to the stream:



Map<String, Map<String, Map<String, Map<String, Double>>>> grouped = salesRecords.stream().collect(Collectors.groupingBy(
SalesRecord::getChannel, TreeMap::new,
Collectors.groupingBy(SalesRecord::getCategory, TreeMap::new,
Collectors.groupingBy(SalesRecord::getBrand, collector))));


The result is a Map<String, Map<String, Map<String, Map<String, Double>>>> grouped and looks like that (added line breaks for readability):



Mini Market= 
Large MM=
OLAY=sum_gross=60.0, sum_qty=8.0,
ARIEL=sum_gross=300.0, sum_qty=9.0,
GILLETTE=sum_gross=103.88, sum_qty=12.0,
Small MM=
OLAY=sum_gross=80.3, sum_qty=3.0,
ORAL B=sum_gross=250.0, sum_qty=13.0,
GILLETTE=sum_gross=50.0, sum_qty=8.0







share|improve this answer























  • Thanks for the answer, I've edited the question and added the missing collector class.

    – Sujal
    Nov 14 '18 at 10:19











  • As far as I see SalesCollector / Accumulator build the sums. Why did you seaprate the code between SalesCollector and Accumulator? They are tightly coupled.

    – LuCio
    Nov 14 '18 at 10:57











  • You asked is it "possible to achive this with current collector result itself with same grouping?" I did it. And: "What would be best way to achieve this?" I don't want to say, my answer shows the best way but I think the code shown has less overhead than SalesCollector / Accumulator.

    – LuCio
    Nov 14 '18 at 11:01












  • So my question now is how do I get the output as List<Pivot> instead and not Map<String, Map<String, Map<String, Map<String, Double>>>> where 'attribute' of Pivot being value of channel/category/brand and aggregates being sum values?

    – Sujal
    Nov 14 '18 at 16:35











  • I'm not sure if I understand your question. The variable grouped corresponds to the field aggregates of Pivot. But I don't know what the purpose of Pivot.attribute is.

    – LuCio
    Nov 14 '18 at 17:20













1












1








1







I extended your stream by the missing collector. It's a dedicated Collector which maps each SalesRecord to a Map<String, Double>. Since Map has one value type I decided to use Double.



 Supplier<Map<String, Double>> supplier = TreeMap::new;
BiConsumer<Map<String, Double>, SalesRecord> biConsumer = (map, sr) ->
map.merge("sum_qty", Double.valueOf(sr.getQty()), (qtySum, qty) -> qtySum + qty);
map.merge("sum_gross", sr.getGross(), (grossSum, gross) -> grossSum + gross);
;
BinaryOperator<Map<String, Double>> binaryOperator = (l, r) ->
l.compute("sum_qty", (k, v) -> v + r.get("sum_qty"));
l.compute("sum_gross", (k, v) -> v + r.get("sum_gross"));
return l;
;
Collector<SalesRecord, Map<String, Double>, Map<String, Double>> collector = Collector.of(supplier, biConsumer, binaryOperator);


Now addded as the last downstream collector to the stream:



Map<String, Map<String, Map<String, Map<String, Double>>>> grouped = salesRecords.stream().collect(Collectors.groupingBy(
SalesRecord::getChannel, TreeMap::new,
Collectors.groupingBy(SalesRecord::getCategory, TreeMap::new,
Collectors.groupingBy(SalesRecord::getBrand, collector))));


The result is a Map<String, Map<String, Map<String, Map<String, Double>>>> grouped and looks like that (added line breaks for readability):



Mini Market= 
Large MM=
OLAY=sum_gross=60.0, sum_qty=8.0,
ARIEL=sum_gross=300.0, sum_qty=9.0,
GILLETTE=sum_gross=103.88, sum_qty=12.0,
Small MM=
OLAY=sum_gross=80.3, sum_qty=3.0,
ORAL B=sum_gross=250.0, sum_qty=13.0,
GILLETTE=sum_gross=50.0, sum_qty=8.0







share|improve this answer













I extended your stream by the missing collector. It's a dedicated Collector which maps each SalesRecord to a Map<String, Double>. Since Map has one value type I decided to use Double.



 Supplier<Map<String, Double>> supplier = TreeMap::new;
BiConsumer<Map<String, Double>, SalesRecord> biConsumer = (map, sr) ->
map.merge("sum_qty", Double.valueOf(sr.getQty()), (qtySum, qty) -> qtySum + qty);
map.merge("sum_gross", sr.getGross(), (grossSum, gross) -> grossSum + gross);
;
BinaryOperator<Map<String, Double>> binaryOperator = (l, r) ->
l.compute("sum_qty", (k, v) -> v + r.get("sum_qty"));
l.compute("sum_gross", (k, v) -> v + r.get("sum_gross"));
return l;
;
Collector<SalesRecord, Map<String, Double>, Map<String, Double>> collector = Collector.of(supplier, biConsumer, binaryOperator);


Now addded as the last downstream collector to the stream:



Map<String, Map<String, Map<String, Map<String, Double>>>> grouped = salesRecords.stream().collect(Collectors.groupingBy(
SalesRecord::getChannel, TreeMap::new,
Collectors.groupingBy(SalesRecord::getCategory, TreeMap::new,
Collectors.groupingBy(SalesRecord::getBrand, collector))));


The result is a Map<String, Map<String, Map<String, Map<String, Double>>>> grouped and looks like that (added line breaks for readability):



Mini Market= 
Large MM=
OLAY=sum_gross=60.0, sum_qty=8.0,
ARIEL=sum_gross=300.0, sum_qty=9.0,
GILLETTE=sum_gross=103.88, sum_qty=12.0,
Small MM=
OLAY=sum_gross=80.3, sum_qty=3.0,
ORAL B=sum_gross=250.0, sum_qty=13.0,
GILLETTE=sum_gross=50.0, sum_qty=8.0








share|improve this answer












share|improve this answer



share|improve this answer










answered Nov 14 '18 at 9:28









LuCioLuCio

2,8041823




2,8041823












  • Thanks for the answer, I've edited the question and added the missing collector class.

    – Sujal
    Nov 14 '18 at 10:19











  • As far as I see SalesCollector / Accumulator build the sums. Why did you seaprate the code between SalesCollector and Accumulator? They are tightly coupled.

    – LuCio
    Nov 14 '18 at 10:57











  • You asked is it "possible to achive this with current collector result itself with same grouping?" I did it. And: "What would be best way to achieve this?" I don't want to say, my answer shows the best way but I think the code shown has less overhead than SalesCollector / Accumulator.

    – LuCio
    Nov 14 '18 at 11:01












  • So my question now is how do I get the output as List<Pivot> instead and not Map<String, Map<String, Map<String, Map<String, Double>>>> where 'attribute' of Pivot being value of channel/category/brand and aggregates being sum values?

    – Sujal
    Nov 14 '18 at 16:35











  • I'm not sure if I understand your question. The variable grouped corresponds to the field aggregates of Pivot. But I don't know what the purpose of Pivot.attribute is.

    – LuCio
    Nov 14 '18 at 17:20

















  • Thanks for the answer, I've edited the question and added the missing collector class.

    – Sujal
    Nov 14 '18 at 10:19











  • As far as I see SalesCollector / Accumulator build the sums. Why did you seaprate the code between SalesCollector and Accumulator? They are tightly coupled.

    – LuCio
    Nov 14 '18 at 10:57











  • You asked is it "possible to achive this with current collector result itself with same grouping?" I did it. And: "What would be best way to achieve this?" I don't want to say, my answer shows the best way but I think the code shown has less overhead than SalesCollector / Accumulator.

    – LuCio
    Nov 14 '18 at 11:01












  • So my question now is how do I get the output as List<Pivot> instead and not Map<String, Map<String, Map<String, Map<String, Double>>>> where 'attribute' of Pivot being value of channel/category/brand and aggregates being sum values?

    – Sujal
    Nov 14 '18 at 16:35











  • I'm not sure if I understand your question. The variable grouped corresponds to the field aggregates of Pivot. But I don't know what the purpose of Pivot.attribute is.

    – LuCio
    Nov 14 '18 at 17:20
















Thanks for the answer, I've edited the question and added the missing collector class.

– Sujal
Nov 14 '18 at 10:19





Thanks for the answer, I've edited the question and added the missing collector class.

– Sujal
Nov 14 '18 at 10:19













As far as I see SalesCollector / Accumulator build the sums. Why did you seaprate the code between SalesCollector and Accumulator? They are tightly coupled.

– LuCio
Nov 14 '18 at 10:57





As far as I see SalesCollector / Accumulator build the sums. Why did you seaprate the code between SalesCollector and Accumulator? They are tightly coupled.

– LuCio
Nov 14 '18 at 10:57













You asked is it "possible to achive this with current collector result itself with same grouping?" I did it. And: "What would be best way to achieve this?" I don't want to say, my answer shows the best way but I think the code shown has less overhead than SalesCollector / Accumulator.

– LuCio
Nov 14 '18 at 11:01






You asked is it "possible to achive this with current collector result itself with same grouping?" I did it. And: "What would be best way to achieve this?" I don't want to say, my answer shows the best way but I think the code shown has less overhead than SalesCollector / Accumulator.

– LuCio
Nov 14 '18 at 11:01














So my question now is how do I get the output as List<Pivot> instead and not Map<String, Map<String, Map<String, Map<String, Double>>>> where 'attribute' of Pivot being value of channel/category/brand and aggregates being sum values?

– Sujal
Nov 14 '18 at 16:35





So my question now is how do I get the output as List<Pivot> instead and not Map<String, Map<String, Map<String, Map<String, Double>>>> where 'attribute' of Pivot being value of channel/category/brand and aggregates being sum values?

– Sujal
Nov 14 '18 at 16:35













I'm not sure if I understand your question. The variable grouped corresponds to the field aggregates of Pivot. But I don't know what the purpose of Pivot.attribute is.

– LuCio
Nov 14 '18 at 17:20





I'm not sure if I understand your question. The variable grouped corresponds to the field aggregates of Pivot. But I don't know what the purpose of Pivot.attribute is.

– LuCio
Nov 14 '18 at 17:20

















draft saved

draft discarded
















































Thanks for contributing an answer to Stack Overflow!


  • Please be sure to answer the question. Provide details and share your research!

But avoid


  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.

To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53293034%2fnesting-values-with-java-8-stream-and-collector%23new-answer', 'question_page');

);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







這個網誌中的熱門文章

Barbados

How to read a connectionString WITH PROVIDER in .NET Core?

Node.js Script on GitHub Pages or Amazon S3