Java8 Stream Collectors - Splitting a list based on sum of values
I am trying partition a list into multiple sublists based on a condition that sum of a particular field should be less than 'x'. Below is sameple code:
public class TestGrouping {
public static class Transaction
String txnId;
String comment;
Amount amount;
public Transaction(String txnId, String comment, Amount amount)
this.txnId = txnId;
this.comment = comment;
this.amount = amount;
public static class Amount
String amountValue;
public Amount(String amountValue)
this.amountValue = amountValue;
public static void main(String args)
List<Transaction> transactionList = new ArrayList<>();
Transaction txn1 = new Transaction("T1","comment1",new Amount("81"));
Transaction txn2 = new Transaction("T2","comment2",new Amount("5"));
Transaction txn3 = new Transaction("T3","comment3",new Amount("12"));
Transaction txn4 = new Transaction("T4","comment4",new Amount("28"));
transactionList.add(txn1);
transactionList.add(txn2);
transactionList.add(txn3);
transactionList.add(txn4);
//below is what i thought might work
// transactionList.stream().collect(groupingBy (r->Collectors.summingInt(Integer.valueOf(r.amount.amountValue)),Collectors.mapping(t -> t, toList())));
The goal is to split the transactionList into 2 (or more) sublists - where the sum of 'amount' is less than 100. So i could have a sublist have only txn1 - having amount as 81; and the other sublist have txn2, txn3, txn4 (as sum of these is less 100). Other possibility is - have sublist1 having txn1, txn2, txn3; and another sublist with just txn4. Not trying to create the most 'optimal' lists basically, just that sum of amounts should be less than 100.
Any clues?
java-8 java-stream collectors
add a comment |
I am trying partition a list into multiple sublists based on a condition that sum of a particular field should be less than 'x'. Below is sameple code:
public class TestGrouping {
public static class Transaction
String txnId;
String comment;
Amount amount;
public Transaction(String txnId, String comment, Amount amount)
this.txnId = txnId;
this.comment = comment;
this.amount = amount;
public static class Amount
String amountValue;
public Amount(String amountValue)
this.amountValue = amountValue;
public static void main(String args)
List<Transaction> transactionList = new ArrayList<>();
Transaction txn1 = new Transaction("T1","comment1",new Amount("81"));
Transaction txn2 = new Transaction("T2","comment2",new Amount("5"));
Transaction txn3 = new Transaction("T3","comment3",new Amount("12"));
Transaction txn4 = new Transaction("T4","comment4",new Amount("28"));
transactionList.add(txn1);
transactionList.add(txn2);
transactionList.add(txn3);
transactionList.add(txn4);
//below is what i thought might work
// transactionList.stream().collect(groupingBy (r->Collectors.summingInt(Integer.valueOf(r.amount.amountValue)),Collectors.mapping(t -> t, toList())));
The goal is to split the transactionList into 2 (or more) sublists - where the sum of 'amount' is less than 100. So i could have a sublist have only txn1 - having amount as 81; and the other sublist have txn2, txn3, txn4 (as sum of these is less 100). Other possibility is - have sublist1 having txn1, txn2, txn3; and another sublist with just txn4. Not trying to create the most 'optimal' lists basically, just that sum of amounts should be less than 100.
Any clues?
java-8 java-stream collectors
1
since you depend on the "next" elements, this is only double with a custom collector - where theaccumulator
would not be very different of what you would do in a plain loop; while thecombiner
would be a bit more fun - but still pretty trivial
– Eugene
Nov 14 '18 at 8:22
add a comment |
I am trying partition a list into multiple sublists based on a condition that sum of a particular field should be less than 'x'. Below is sameple code:
public class TestGrouping {
public static class Transaction
String txnId;
String comment;
Amount amount;
public Transaction(String txnId, String comment, Amount amount)
this.txnId = txnId;
this.comment = comment;
this.amount = amount;
public static class Amount
String amountValue;
public Amount(String amountValue)
this.amountValue = amountValue;
public static void main(String args)
List<Transaction> transactionList = new ArrayList<>();
Transaction txn1 = new Transaction("T1","comment1",new Amount("81"));
Transaction txn2 = new Transaction("T2","comment2",new Amount("5"));
Transaction txn3 = new Transaction("T3","comment3",new Amount("12"));
Transaction txn4 = new Transaction("T4","comment4",new Amount("28"));
transactionList.add(txn1);
transactionList.add(txn2);
transactionList.add(txn3);
transactionList.add(txn4);
//below is what i thought might work
// transactionList.stream().collect(groupingBy (r->Collectors.summingInt(Integer.valueOf(r.amount.amountValue)),Collectors.mapping(t -> t, toList())));
The goal is to split the transactionList into 2 (or more) sublists - where the sum of 'amount' is less than 100. So i could have a sublist have only txn1 - having amount as 81; and the other sublist have txn2, txn3, txn4 (as sum of these is less 100). Other possibility is - have sublist1 having txn1, txn2, txn3; and another sublist with just txn4. Not trying to create the most 'optimal' lists basically, just that sum of amounts should be less than 100.
Any clues?
java-8 java-stream collectors
I am trying partition a list into multiple sublists based on a condition that sum of a particular field should be less than 'x'. Below is sameple code:
public class TestGrouping {
public static class Transaction
String txnId;
String comment;
Amount amount;
public Transaction(String txnId, String comment, Amount amount)
this.txnId = txnId;
this.comment = comment;
this.amount = amount;
public static class Amount
String amountValue;
public Amount(String amountValue)
this.amountValue = amountValue;
public static void main(String args)
List<Transaction> transactionList = new ArrayList<>();
Transaction txn1 = new Transaction("T1","comment1",new Amount("81"));
Transaction txn2 = new Transaction("T2","comment2",new Amount("5"));
Transaction txn3 = new Transaction("T3","comment3",new Amount("12"));
Transaction txn4 = new Transaction("T4","comment4",new Amount("28"));
transactionList.add(txn1);
transactionList.add(txn2);
transactionList.add(txn3);
transactionList.add(txn4);
//below is what i thought might work
// transactionList.stream().collect(groupingBy (r->Collectors.summingInt(Integer.valueOf(r.amount.amountValue)),Collectors.mapping(t -> t, toList())));
The goal is to split the transactionList into 2 (or more) sublists - where the sum of 'amount' is less than 100. So i could have a sublist have only txn1 - having amount as 81; and the other sublist have txn2, txn3, txn4 (as sum of these is less 100). Other possibility is - have sublist1 having txn1, txn2, txn3; and another sublist with just txn4. Not trying to create the most 'optimal' lists basically, just that sum of amounts should be less than 100.
Any clues?
java-8 java-stream collectors
java-8 java-stream collectors
edited Nov 14 '18 at 17:33
Stefan Zobel
2,47231830
2,47231830
asked Nov 14 '18 at 8:18
RahulRahul
62
62
1
since you depend on the "next" elements, this is only double with a custom collector - where theaccumulator
would not be very different of what you would do in a plain loop; while thecombiner
would be a bit more fun - but still pretty trivial
– Eugene
Nov 14 '18 at 8:22
add a comment |
1
since you depend on the "next" elements, this is only double with a custom collector - where theaccumulator
would not be very different of what you would do in a plain loop; while thecombiner
would be a bit more fun - but still pretty trivial
– Eugene
Nov 14 '18 at 8:22
1
1
since you depend on the "next" elements, this is only double with a custom collector - where the
accumulator
would not be very different of what you would do in a plain loop; while the combiner
would be a bit more fun - but still pretty trivial– Eugene
Nov 14 '18 at 8:22
since you depend on the "next" elements, this is only double with a custom collector - where the
accumulator
would not be very different of what you would do in a plain loop; while the combiner
would be a bit more fun - but still pretty trivial– Eugene
Nov 14 '18 at 8:22
add a comment |
1 Answer
1
active
oldest
votes
The Idea is to use a custom collector to generate a list of pair(amountSum, transactions), the list should initialy be sorted. The accumulator method (here Accumulator.accept) do the grouping logic, I didn't implement combine because there is no need for a combiner in non parallel stream.
Bellow the code snippet, hope it helps.
public class TestStream
public class Transaction
String txnId;
String comment;
Amount amount;
public Transaction(String txnId, String comment, Amount amount)
this.txnId = txnId;
this.comment = comment;
this.amount = amount;
public class Amount
String amountValue;
public Amount(String amountValue)
this.amountValue = amountValue;
@Test
public void test()
List<Transaction> transactionList = new ArrayList<>();
Transaction txn1 = new Transaction("T1", "comment1", new Amount("81"));
Transaction txn2 = new Transaction("T2", "comment2", new Amount("5"));
Transaction txn3 = new Transaction("T3", "comment3", new Amount("12"));
Transaction txn4 = new Transaction("T4", "comment4", new Amount("28"));
transactionList.add(txn1);
transactionList.add(txn2);
transactionList.add(txn3);
transactionList.add(txn4);
transactionList.stream()
.sorted(Comparator.comparing(tr -> Integer.valueOf(tr.amount.amountValue)))
.collect(ArrayList<Pair<Integer, List<Transaction>>>::new, Accumulator::accept, (x, y) ->
)
.forEach(t ->
System.out.println(t.left);
);
static class Accumulator
public static void accept(List<Pair<Integer, List<Transaction>>> lPair, Transaction tr) lastPair.left + amount > 100)
lPair.add(
new TestStream().new Pair<Integer, List<Transaction>>(amount,
Arrays.asList(tr)));
else
List<Transaction> newList = new ArrayList<>();
newList.addAll(lastPair.getRight());
newList.add(tr);
lastPair.setLeft(lastPair.getLeft() + amount);
lastPair.setRight(newList);
class Pair<T, V>
private T left;
private V right;
/**
*
*/
public Pair(T left, V right)
this.left = left;
this.right = right;
public V getRight()
return right;
public T getLeft()
return left;
public void setLeft(T left)
this.left = left;
public void setRight(V right)
this.right = right;
add a comment |
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%2f53295684%2fjava8-stream-collectors-splitting-a-list-based-on-sum-of-values%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
The Idea is to use a custom collector to generate a list of pair(amountSum, transactions), the list should initialy be sorted. The accumulator method (here Accumulator.accept) do the grouping logic, I didn't implement combine because there is no need for a combiner in non parallel stream.
Bellow the code snippet, hope it helps.
public class TestStream
public class Transaction
String txnId;
String comment;
Amount amount;
public Transaction(String txnId, String comment, Amount amount)
this.txnId = txnId;
this.comment = comment;
this.amount = amount;
public class Amount
String amountValue;
public Amount(String amountValue)
this.amountValue = amountValue;
@Test
public void test()
List<Transaction> transactionList = new ArrayList<>();
Transaction txn1 = new Transaction("T1", "comment1", new Amount("81"));
Transaction txn2 = new Transaction("T2", "comment2", new Amount("5"));
Transaction txn3 = new Transaction("T3", "comment3", new Amount("12"));
Transaction txn4 = new Transaction("T4", "comment4", new Amount("28"));
transactionList.add(txn1);
transactionList.add(txn2);
transactionList.add(txn3);
transactionList.add(txn4);
transactionList.stream()
.sorted(Comparator.comparing(tr -> Integer.valueOf(tr.amount.amountValue)))
.collect(ArrayList<Pair<Integer, List<Transaction>>>::new, Accumulator::accept, (x, y) ->
)
.forEach(t ->
System.out.println(t.left);
);
static class Accumulator
public static void accept(List<Pair<Integer, List<Transaction>>> lPair, Transaction tr) lastPair.left + amount > 100)
lPair.add(
new TestStream().new Pair<Integer, List<Transaction>>(amount,
Arrays.asList(tr)));
else
List<Transaction> newList = new ArrayList<>();
newList.addAll(lastPair.getRight());
newList.add(tr);
lastPair.setLeft(lastPair.getLeft() + amount);
lastPair.setRight(newList);
class Pair<T, V>
private T left;
private V right;
/**
*
*/
public Pair(T left, V right)
this.left = left;
this.right = right;
public V getRight()
return right;
public T getLeft()
return left;
public void setLeft(T left)
this.left = left;
public void setRight(V right)
this.right = right;
add a comment |
The Idea is to use a custom collector to generate a list of pair(amountSum, transactions), the list should initialy be sorted. The accumulator method (here Accumulator.accept) do the grouping logic, I didn't implement combine because there is no need for a combiner in non parallel stream.
Bellow the code snippet, hope it helps.
public class TestStream
public class Transaction
String txnId;
String comment;
Amount amount;
public Transaction(String txnId, String comment, Amount amount)
this.txnId = txnId;
this.comment = comment;
this.amount = amount;
public class Amount
String amountValue;
public Amount(String amountValue)
this.amountValue = amountValue;
@Test
public void test()
List<Transaction> transactionList = new ArrayList<>();
Transaction txn1 = new Transaction("T1", "comment1", new Amount("81"));
Transaction txn2 = new Transaction("T2", "comment2", new Amount("5"));
Transaction txn3 = new Transaction("T3", "comment3", new Amount("12"));
Transaction txn4 = new Transaction("T4", "comment4", new Amount("28"));
transactionList.add(txn1);
transactionList.add(txn2);
transactionList.add(txn3);
transactionList.add(txn4);
transactionList.stream()
.sorted(Comparator.comparing(tr -> Integer.valueOf(tr.amount.amountValue)))
.collect(ArrayList<Pair<Integer, List<Transaction>>>::new, Accumulator::accept, (x, y) ->
)
.forEach(t ->
System.out.println(t.left);
);
static class Accumulator
public static void accept(List<Pair<Integer, List<Transaction>>> lPair, Transaction tr) lastPair.left + amount > 100)
lPair.add(
new TestStream().new Pair<Integer, List<Transaction>>(amount,
Arrays.asList(tr)));
else
List<Transaction> newList = new ArrayList<>();
newList.addAll(lastPair.getRight());
newList.add(tr);
lastPair.setLeft(lastPair.getLeft() + amount);
lastPair.setRight(newList);
class Pair<T, V>
private T left;
private V right;
/**
*
*/
public Pair(T left, V right)
this.left = left;
this.right = right;
public V getRight()
return right;
public T getLeft()
return left;
public void setLeft(T left)
this.left = left;
public void setRight(V right)
this.right = right;
add a comment |
The Idea is to use a custom collector to generate a list of pair(amountSum, transactions), the list should initialy be sorted. The accumulator method (here Accumulator.accept) do the grouping logic, I didn't implement combine because there is no need for a combiner in non parallel stream.
Bellow the code snippet, hope it helps.
public class TestStream
public class Transaction
String txnId;
String comment;
Amount amount;
public Transaction(String txnId, String comment, Amount amount)
this.txnId = txnId;
this.comment = comment;
this.amount = amount;
public class Amount
String amountValue;
public Amount(String amountValue)
this.amountValue = amountValue;
@Test
public void test()
List<Transaction> transactionList = new ArrayList<>();
Transaction txn1 = new Transaction("T1", "comment1", new Amount("81"));
Transaction txn2 = new Transaction("T2", "comment2", new Amount("5"));
Transaction txn3 = new Transaction("T3", "comment3", new Amount("12"));
Transaction txn4 = new Transaction("T4", "comment4", new Amount("28"));
transactionList.add(txn1);
transactionList.add(txn2);
transactionList.add(txn3);
transactionList.add(txn4);
transactionList.stream()
.sorted(Comparator.comparing(tr -> Integer.valueOf(tr.amount.amountValue)))
.collect(ArrayList<Pair<Integer, List<Transaction>>>::new, Accumulator::accept, (x, y) ->
)
.forEach(t ->
System.out.println(t.left);
);
static class Accumulator
public static void accept(List<Pair<Integer, List<Transaction>>> lPair, Transaction tr) lastPair.left + amount > 100)
lPair.add(
new TestStream().new Pair<Integer, List<Transaction>>(amount,
Arrays.asList(tr)));
else
List<Transaction> newList = new ArrayList<>();
newList.addAll(lastPair.getRight());
newList.add(tr);
lastPair.setLeft(lastPair.getLeft() + amount);
lastPair.setRight(newList);
class Pair<T, V>
private T left;
private V right;
/**
*
*/
public Pair(T left, V right)
this.left = left;
this.right = right;
public V getRight()
return right;
public T getLeft()
return left;
public void setLeft(T left)
this.left = left;
public void setRight(V right)
this.right = right;
The Idea is to use a custom collector to generate a list of pair(amountSum, transactions), the list should initialy be sorted. The accumulator method (here Accumulator.accept) do the grouping logic, I didn't implement combine because there is no need for a combiner in non parallel stream.
Bellow the code snippet, hope it helps.
public class TestStream
public class Transaction
String txnId;
String comment;
Amount amount;
public Transaction(String txnId, String comment, Amount amount)
this.txnId = txnId;
this.comment = comment;
this.amount = amount;
public class Amount
String amountValue;
public Amount(String amountValue)
this.amountValue = amountValue;
@Test
public void test()
List<Transaction> transactionList = new ArrayList<>();
Transaction txn1 = new Transaction("T1", "comment1", new Amount("81"));
Transaction txn2 = new Transaction("T2", "comment2", new Amount("5"));
Transaction txn3 = new Transaction("T3", "comment3", new Amount("12"));
Transaction txn4 = new Transaction("T4", "comment4", new Amount("28"));
transactionList.add(txn1);
transactionList.add(txn2);
transactionList.add(txn3);
transactionList.add(txn4);
transactionList.stream()
.sorted(Comparator.comparing(tr -> Integer.valueOf(tr.amount.amountValue)))
.collect(ArrayList<Pair<Integer, List<Transaction>>>::new, Accumulator::accept, (x, y) ->
)
.forEach(t ->
System.out.println(t.left);
);
static class Accumulator
public static void accept(List<Pair<Integer, List<Transaction>>> lPair, Transaction tr) lastPair.left + amount > 100)
lPair.add(
new TestStream().new Pair<Integer, List<Transaction>>(amount,
Arrays.asList(tr)));
else
List<Transaction> newList = new ArrayList<>();
newList.addAll(lastPair.getRight());
newList.add(tr);
lastPair.setLeft(lastPair.getLeft() + amount);
lastPair.setRight(newList);
class Pair<T, V>
private T left;
private V right;
/**
*
*/
public Pair(T left, V right)
this.left = left;
this.right = right;
public V getRight()
return right;
public T getLeft()
return left;
public void setLeft(T left)
this.left = left;
public void setRight(V right)
this.right = right;
answered Nov 14 '18 at 12:18
hasnaehasnae
1,2651016
1,2651016
add a comment |
add a comment |
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%2f53295684%2fjava8-stream-collectors-splitting-a-list-based-on-sum-of-values%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
1
since you depend on the "next" elements, this is only double with a custom collector - where the
accumulator
would not be very different of what you would do in a plain loop; while thecombiner
would be a bit more fun - but still pretty trivial– Eugene
Nov 14 '18 at 8:22