0

I am trying to sort the list of objects with following parameters

a.group the two list objects whose partId is same as id of other object

b. Push any object whose partId is null to the end of the list.

c. sort the grouped objects based on the count in ascending order, preference is to

I. both the objects in the group should have less count

II. then any object in group having less count than other groups.

Below is the code I have so far,

public class ListTTest {
    public static void main(String[] args) {
        LstObj lstObj1 = new LstObj("0:0:1", "1:0:1", 49);
        LstObj lstObj2 = new LstObj("0:0:2", "1:0:2", 2);
        LstObj lstObj3 = new LstObj("0:2:1", "1:2:1", 0);
        LstObj lstObj4 = new LstObj("0:2:2", null , 0);
        LstObj lstObj5 = new LstObj("0:2:3", "1:2:3" , 2);
        LstObj lstObj6 = new LstObj("0:2:4", "1:2:4" , 49);
        LstObj lstObj7 = new LstObj("1:0:1", "0:0:1" , 49);
        LstObj lstObj8 = new LstObj("1:0:2", "0:0:2" , 49);
        LstObj lstObj9 = new LstObj("1:2:1", "0:2:1" , 0);
        LstObj lstObj10 = new LstObj("1:2:2", null , 0);
        LstObj lstObj11 = new LstObj("1:2:3", "0:2:3" , 49);
        LstObj lstObj12 = new LstObj("1:2:4", "0:2:4" , 49);


        LstObj lst[] = new LstObj[]{lstObj1,lstObj2,lstObj3,lstObj4,lstObj5,lstObj6,lstObj7,lstObj8,lstObj9,lstObj10,lstObj11,lstObj12};
        List<LstObj> lstArr = Arrays.asList(lst);

        lstArr.sort(new Comparator<LstObj>() {
            @Override
            public int compare(LstObj o1, LstObj o2) {
                    if(o1.partId==null){
                        return 1;
                    }else if(o2.partId==null){
                         return -1;
                    }else{
                        return -1*(o1.partId.compareTo(o2.id)-(o1.count-o2.count));
                    }

            }
        });

        System.out.println(lstArr);
    }


}

class LstObj {
    String partId;
    String id;
    int count;

    public LstObj(  String id,
    String partId,
    int count
    ) {
        this.count = count;
        this.partId = partId;
        this.id = id;       
    }

    public String getPartId() {
        return partId;
    }
    public String getId() {
        return id;
    }
    public int getCount() {
        return count;
    }
    public void setPartId(String partId) {
        this.partId = partId;
    }
    public void setId(String id) {
        this.id = id;
    }
    public void setCount(int count) {
        this.count = count;
    }

    @Override
    public String toString() {
        return "LstObj [partId=" + partId + ", id=" + id + ", count=" + count
                + "]\n";
    }


}

Output for above code is :

[LstObj [partId=1:2:1, id=0:2:1, count=0]
    , LstObj [partId=1:2:3, id=0:2:3, count=2]
    , LstObj [partId=0:2:1, id=1:2:1, count=0]
    , LstObj [partId=1:0:2, id=0:0:2, count=2]
    , LstObj [partId=1:2:4, id=0:2:4, count=49]
    , LstObj [partId=0:2:3, id=1:2:3, count=49]
    , LstObj [partId=1:0:1, id=0:0:1, count=49]
    , LstObj [partId=0:0:1, id=1:0:1, count=49]
    , LstObj [partId=0:0:2, id=1:0:2, count=49]
    , LstObj [partId=0:2:4, id=1:2:4, count=49]
    , LstObj [partId=null, id=0:2:2, count=0]
    , LstObj [partId=null, id=1:2:2, count=0]

But I am looking for output as:

 [LstObj [partId=0:2:1, id=1:2:1, count=0]
    , LstObj [partId=1:2:1, id=0:2:1, count=0]
    , LstObj [partId=0:0:2, id=1:0:2, count=2]
    , LstObj [partId=1:0:2, id=0:0:2, count=49]
    , LstObj [partId=0:2:3, id=1:2:3, count=2]
    , LstObj [partId=1:2:3, id=0:2:3, count=49]
    , LstObj [partId=0:0:1, id=1:0:1, count=49]
    , LstObj [partId=1:0:1, id=0:0:1, count=49]
    , LstObj [partId=0:2:4, id=1:2:4, count=49]
    , LstObj [partId=1:2:4, id=0:2:4, count=49]
    , LstObj [partId=null, id=0:2:2, count=0]
    , LstObj [partId=null, id=1:2:2, count=0]

Anybody have any idea where exactly I'm doing wrong?

2
  • 2
    Not sure where the error is, but you ought to take a look at the methods that were added to the Comparator interface in Java 8, e.g. comparing(), thenComparing() and nullsLast(). I believe they can simplify your code quite a bit. Commented Mar 19, 2018 at 13:59
  • A comparator should be symmetric. A term like o1.partId.compareTo(o2.id) can’t be correct. Further, combining two comparison results via minus like with o1.partId.compareTo(o2.id)-(o1.count-o2.count) makes no sense, as the magnitude of the value returned by compareTo is entirely unspecified (and what’s the supposed result anyway). Further, -1*(…) is a verbose way of flipping the sign (-(…) would do as well), which is a broken way of reversing a comparator. If the comparator evaluates to Integer.MIN_VALUE, flipping the sign causes an overflow, resulting in Integer.MIN_VALUE again… Commented Mar 19, 2018 at 16:46

1 Answer 1

2

It is impossible to implement a comparator applying your intended grouping logic by just looking at two elements of the list.

You may perform a dedicated grouping operation and sort the groups, followed by creating a result list afterwards. The builtin grouping collector requires a function that is capable of assigning each element to a group without seeing the counterpart, but if you use a stable selector, e.g. using the minimum of partId and id, the correct elements will end up in one group, as long as your input data has the right shape.

List<LstObj> lstArr = Arrays.asList(
    new LstObj("0:0:1", "1:0:1", 49),
    new LstObj("0:0:2", "1:0:2", 2),
    new LstObj("0:2:1", "1:2:1", 0),
    new LstObj("0:2:2", null , 0),
    new LstObj("0:2:3", "1:2:3" , 2),
    new LstObj("0:2:4", "1:2:4" , 49),
    new LstObj("1:0:1", "0:0:1" , 49),
    new LstObj("1:0:2", "0:0:2" , 49),
    new LstObj("1:2:1", "0:2:1" , 0),
    new LstObj("1:2:2", null , 0),
    new LstObj("1:2:3", "0:2:3" , 49),
    new LstObj("1:2:4", "0:2:4" , 49)
);

Function<LstObj,Object> groupFunc = o -> {
    String id = o.getId(), partId = o.getPartId();
    return partId == null? Void.TYPE: partId.compareTo(id)<0? partId: id;
};
List<LstObj> result = lstArr.stream()
        .sorted(Comparator.comparing(LstObj::getPartId, Comparator.nullsLast(null))
                          .thenComparingInt(LstObj::getCount))
        .collect(Collectors.groupingBy(groupFunc, LinkedHashMap::new, Collectors.toList()))
        .values().stream()
        .flatMap(List::stream)
        .collect(Collectors.toList());
System.out.println(result);

This sorts all elements, moving all objects whose partId is null to the end of the list and sorting by count as secondary order. The subsequent grouping operation uses a LinkedHashMap which retains the encounter order, effectively sorting the groups according to their first encountered element, which is the one with the minimum count. The groups are then flattened to a result list.

The result fulfills the specified criteria of your question but does not exactly produce the list you have posted as desired result, as the order of the elements within the groups is underspecified. The code above sorts them by count, as a side effect of initial sorting, and keeps the original list order for elements with the same count.

Sign up to request clarification or add additional context in comments.

4 Comments

Elaborative explanation @Holger, thanks for the learning pointers.
the code above is working almost fine but the how do we reorder the groups based on sum of count among the groups? [0,0][1,7][1,6] as counts should be reordered as [0,0][1,6][1,7]. Do you have any idea?
Then, you would need to sort the groups after the sorting, e.g. change the tail of above solution to … .values().stream() .sorted(Comparator.comparing(l -> l.stream().mapToInt(LstObj::getCount).sum())) .flatMap(List::stream) .collect(Collectors.toList()). Though, this would not respect the “null partIds to the end”constraint. When reaching that complexity, removing those special objects and re-adding them afterwards would be the simpler choice.
Yeah, that would be simpler. Thank you

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.