Nesting Values with Java 8 stream and collector
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
add a comment |
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
Could you share thePivotTable
andSalesRecord
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
add a comment |
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
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
java java-stream collectors
edited Nov 14 '18 at 10:18
Sujal
asked Nov 14 '18 at 4:03
SujalSujal
3291925
3291925
Could you share thePivotTable
andSalesRecord
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
add a comment |
Could you share thePivotTable
andSalesRecord
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
add a comment |
1 Answer
1
active
oldest
votes
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
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 seeSalesCollector
/Accumulator
build the sums. Why did you seaprate the code betweenSalesCollector
andAccumulator
? 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 thanSalesCollector
/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 variablegrouped
corresponds to the fieldaggregates
ofPivot
. But I don't know what the purpose ofPivot.attribute
is.
– LuCio
Nov 14 '18 at 17:20
|
show 2 more comments
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
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
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
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 seeSalesCollector
/Accumulator
build the sums. Why did you seaprate the code betweenSalesCollector
andAccumulator
? 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 thanSalesCollector
/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 variablegrouped
corresponds to the fieldaggregates
ofPivot
. But I don't know what the purpose ofPivot.attribute
is.
– LuCio
Nov 14 '18 at 17:20
|
show 2 more comments
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
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 seeSalesCollector
/Accumulator
build the sums. Why did you seaprate the code betweenSalesCollector
andAccumulator
? 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 thanSalesCollector
/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 variablegrouped
corresponds to the fieldaggregates
ofPivot
. But I don't know what the purpose ofPivot.attribute
is.
– LuCio
Nov 14 '18 at 17:20
|
show 2 more comments
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
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
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 seeSalesCollector
/Accumulator
build the sums. Why did you seaprate the code betweenSalesCollector
andAccumulator
? 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 thanSalesCollector
/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 variablegrouped
corresponds to the fieldaggregates
ofPivot
. But I don't know what the purpose ofPivot.attribute
is.
– LuCio
Nov 14 '18 at 17:20
|
show 2 more comments
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 seeSalesCollector
/Accumulator
build the sums. Why did you seaprate the code betweenSalesCollector
andAccumulator
? 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 thanSalesCollector
/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 variablegrouped
corresponds to the fieldaggregates
ofPivot
. But I don't know what the purpose ofPivot.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
|
show 2 more comments
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.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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
Could you share the
PivotTable
andSalesRecord
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