Java8 Stream Collectors - Splitting a list based on sum of values










1















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?










share|improve this question



















  • 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















1















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?










share|improve this question



















  • 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













1












1








1








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?










share|improve this question
















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






share|improve this question















share|improve this question













share|improve this question




share|improve this question








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 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












  • 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







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












1 Answer
1






active

oldest

votes


















0














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;









share|improve this answer






















    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%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









    0














    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;









    share|improve this answer



























      0














      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;









      share|improve this answer

























        0












        0








        0







        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;









        share|improve this answer













        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;










        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered Nov 14 '18 at 12:18









        hasnaehasnae

        1,2651016




        1,2651016





























            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%2f53295684%2fjava8-stream-collectors-splitting-a-list-based-on-sum-of-values%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







            Popular posts from this blog

            How to how show current date and time by default on contact form 7 in WordPress without taking input from user in datetimepicker

            Syphilis

            Darth Vader #20