3

I need your help,

In Java 8 using Collectors groupingBy I need to group a list like this

ValueObject {id=1, value=2.0}
ValueObject {id=2, value=2.0}
ValueObject {id=3, value=2.0}
ValueObject {id=4, value=3.0}
ValueObject {id=5, value=3.0}
ValueObject {id=6, value=4.0}
ValueObject {id=7, value=4.0}
ValueObject {id=8, value=4.0}
ValueObject {id=9, value=4.0}
ValueObject {id=10, value=4.0}

in another one like this

GroupedObject {from=1, to=3, value=2.0}
GroupedObject {from=4, to=5, value=3.0}
GroupedObject {from=6, to=10, value=4.0}

Those are the definitions of the objects i'm using

public class ValueObject {

  private int id;
  private double value;

  public String getId() {
    return id;
  }

  public float getValue() {
    return value;
  }

  public void setValue(float value) {
    this.value = value;
  }

}

public class GroupedObject {

    private int from;
    private int to;
    private double value;

    public int getFrom() {
        return from;
    }

    public void setFrom(int from) {
        this.from = from;
    }

    public int getTo() {
        return to;
    }

    public void setTo(int to) {
        this.to = to;
    }

    public double getValue() {
        return value;
    }

    public void setValue(double value) {
        this.value = value;
    }

}

And this is how i'm doing it programmatically.

public class Service {

    public List<GroupedObject> groupToRange(List<ValueObject> list) {

        List<GroupedObject> filtered = new ArrayList<>();

        if (list.size() > 0) {

            ValueObject current = list.get(0);
            GroupedObject dto = new GroupedObject();
            dto.setValue(current.getValue());
            dto.setFrom(current.getId());

            for (int i = 0; i < list.size(); i++) {
                ValueObject vo = list.get(i);
                if (vo.getValue() != current.getValue()) {

                    dto.setTo(current.getId());
                    filtered.add(dto);

                    dto = new GroupedObject();
                    dto.setValue(vo.getValue());
                    dto.setFrom(vo.getId());
                    current = vo;

                } else {
                    current = vo;
                }
                if (i == list.size() - 1) {
                    dto.setTo(vo.getId());
                    filtered.add(dto);
                }
            }
        }
        return filtered;
    }

}

this is the unit test

public class ServiceTest {

    Service service = new Service();

    @Test
    public void testgGoupToRange() {

        List entryList = new ArrayList<>();

        entryList.add(new ValueObject(1, 2.0));
        entryList.add(new ValueObject(2, 2.0));
        entryList.add(new ValueObject(3, 2.0));
        entryList.add(new ValueObject(4, 3.0));
        entryList.add(new ValueObject(5, 3.0));
        entryList.add(new ValueObject(6, 4.0));
        entryList.add(new ValueObject(7, 4.0));
        entryList.add(new ValueObject(8, 4.0));
        entryList.add(new ValueObject(9, 4.0));
        entryList.add(new ValueObject(10, 4.0));

        List responseList = service.groupToRange(entryList);

        responseList.forEach(obj-> System.out.println(obj.toString()));

        assertNotNull(responseList);
        assertEquals(3, responseList.size());

    }

}

I havn´t found a way of doing it whit java 8 and collectors

4
  • 1
    Yes, you write software to do it. Please invest the least effort before tossing your problem to others to solve for you. Commented Aug 2, 2016 at 15:42
  • Sorry, i already did it. But i wanna know if there is a way using Collectors groupingBy. Commented Aug 2, 2016 at 15:45
  • 1
    The provided ValueObject definition does not compile. Commented Aug 2, 2016 at 16:31
  • 1
    Sorry about that, i fixed it. I have provided the unit test too. Commented Aug 2, 2016 at 17:03

1 Answer 1

2

Here's what I came up with

List<ValueObject> values = Arrays.asList(new ValueObject(1, 2.0),
                                         new ValueObject(2, 2.0),
                                         new ValueObject(3, 3.0),
                                         new ValueObject(4, 4.0),
                                         new ValueObject(5, 4.0),
                                         new ValueObject(6, 4.0));
Map<Double, IntSummaryStatistics> groupedValues = values.stream()
                                                        .collect(Collectors.groupingBy(ValueObject::getValue,
                                                                                       Collectors.summarizingInt(ValueObject::getId)));

List<GroupedObject> groupedObjects = groupedValues.entrySet()
                                                  .stream()
                                                  .map(groupedValue -> new GroupedObject(groupedValue.getValue().getMin(),
                                                                                  groupedValue.getValue().getMax(),
                                                                                  groupedValue.getKey()))
                                                  .collect(Collectors.toList());
System.out.println(groupedObjects);

I'm pretty confident that there's a way to avoid the intermediary Map<Double, IntSummaryStatistics but I haven't figured it out yet. Will update if I do.

EDIT: See @Tunaki's answer for a 1 pass answer.

This is the output

[GroupedObject{from=4, to=6, value=4.0}, GroupedObject{from=1, to=2, value=2.0}, GroupedObject{from=3, to=3, value=3.0}]
Sign up to request clarification or add additional context in comments.

2 Comments

There is a way with only making 1 pass but it isn't that straight-forward (see my answer to a related question). Best would be to stick with this.
Really thanks, this is a really good approach of what i was looking for. i added a sort expression for retrieve the final group in order. .sorted(Comparator.comparing(GroupedObject::getFrom))

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.