Collections.Sort with Multiple Fields

Collections.sort with multiple fields

Do you see anything wrong with the code?

Yes. Why are you adding the three fields together before you compare them?

I would probably do something like this: (assuming the fields are in the order you wish to sort them in)

@Override public int compare(final Report record1, final Report record2) {
int c;
c = record1.getReportKey().compareTo(record2.getReportKey());
if (c == 0)
c = record1.getStudentNumber().compareTo(record2.getStudentNumber());
if (c == 0)
c = record1.getSchool().compareTo(record2.getSchool());
return c;
}

How to sort by two fields in Java?

You can use Collections.sort as follows:

private static void order(List<Person> persons) {

Collections.sort(persons, new Comparator() {

public int compare(Object o1, Object o2) {

String x1 = ((Person) o1).getName();
String x2 = ((Person) o2).getName();
int sComp = x1.compareTo(x2);

if (sComp != 0) {
return sComp;
}

Integer x1 = ((Person) o1).getAge();
Integer x2 = ((Person) o2).getAge();
return x1.compareTo(x2);
}});
}

List<Persons> is now sorted by name, then by age.

String.compareTo "Compares two strings lexicographically" - from the docs.

Collections.sort is a static method in the native Collections library. It does the actual sorting, you just need to provide a Comparator which defines how two elements in your list should be compared: this is achieved by providing your own implementation of the compare method.

Collections.sort for multiple conditions

You can use Java Streams. This is also used when using Collection.sort():

myList.sort(Comparator.comparing(MyObject::getAttributeX)
.thenComparing(i -> i.getSomething().getSubValue())
.thenComparing((a, b) -> a.getInt() - b.getInt()));

If you are using a lower version than Java 8 you have to implement the sort logic yourself in a Comparator or use an external library:

Collections.sort(myList, new Comparator<MyObject>() {
@Override
public int compare(MyObject a, MyObject b) {
int cmp0 = a.getAttributeX().compareTo(b.getAttributeX());
if (cmp0 != 0) {
return cmp0;
}
int cmp1 = a.getSomething().getSubValue().compareTo(b.getSomething().getSubValue());
if (cmp1 != 0) {
return cmp1;
}
return a.getInt() - b.getInt();
}
});

How to sort a list by multiple fields in different orders (asc/desc) in Java?

Comparator.reversed() for descending order

    Comparator<Element> compar = Comparator.comparing(Element::getA)
.thenComparing(Comparator.comparing(Element::getB).reversed())
.thenComparing(Element::getC);
yourArrayList.sort(compar);

In addition to the reversed method I am exploiting the fact that thenComparing() is overloaded: one thenComparing() takes a Comparator as argument, which we need for reversing, the other just takes a method reference (or lambda) as argument (a Function in the declaration of thenComparing()).

If either a, b or c is a primitive int, long or double remember to use comparingInt(), comparingLong(), comparingDouble(), thenComparingInt(), etc.

Sort a List of objects by multiple fields

Your Comparator would look like this:

public class GraduationCeremonyComparator implements Comparator<GraduationCeremony> {
public int compare(GraduationCeremony o1, GraduationCeremony o2) {
int value1 = o1.campus.compareTo(o2.campus);
if (value1 == 0) {
int value2 = o1.faculty.compareTo(o2.faculty);
if (value2 == 0) {
return o1.building.compareTo(o2.building);
} else {
return value2;
}
}
return value1;
}
}

Basically it continues comparing each successive attribute of your class whenever the compared attributes so far are equal (== 0).

Sort a list of Java objects by multiple fields and group by a particular field

The solution you have posted seems to be too complex for this problem. Given below is a clean approach for solving it:

  1. Sort employees using the comparators defined in the class, Solution.
  2. Group employees by group ID with the sum of salary as the grouping function. In other words, create a Map in which groupId will be the key and the sum of salaries pertaining to the groupId will be the value.
  3. Iterate the sorted entry set of map created in step#2 and put the records corresponding to each entry into the result list.

    Given below is the code implementing the above-mentioned algorithm:

    // Sort employees using the comparators defined in the class, Solution
    new Solution().sortEmployees(empList);

    // Group employees by group ID with the sum of salary as the grouping function
    Map<String, Integer> map = new HashMap<>();
    for (Employee e : empList) {
    String grp = e.getGroupId();
    if (grp == null) {
    grp = "null";
    }
    Integer salary = map.get(grp);
    map.put(grp, salary == null ? e.getSalary() : e.getSalary() + salary);
    }

    // Result list
    List<Employee> result = new ArrayList<>();

    // Iterate the sorted entry set of `map` and put the records corresponding to
    // an entry into the result list
    for (Entry<String, Integer> entry : entriesSortedByValues(map)) {
    String grp = entry.getKey();
    int i;

    // Find the starting index of `grp` in empList

    if ("null".equals(grp)) {// Special handling for employees with `null` group
    // Find the index in `empList` where employees with the group as `null` starts
    for (i = 0; i < empList.size() && empList.get(i).getGroupId() != null; i++)
    ;

    // Add elements before a different group is encountered
    for (int j = i; j < empList.size() && empList.get(j).getGroupId() == null; j++) {
    result.add(empList.get(j));
    }
    } else {
    // Find the index in `empList` where employees with the group as `grp` starts
    for (i = 0; i < empList.size() && !grp.equals(empList.get(i).getGroupId()); i++)
    ;

    // Add elements before a different group is encountered
    for (int j = i; j < empList.size() && grp.equals(empList.get(j).getGroupId()); j++) {
    result.add(empList.get(j));
    }
    }
    }

    Demo

    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.Collections;
    import java.util.Comparator;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    import java.util.Map.Entry;
    import java.util.Objects;
    import java.util.SortedSet;
    import java.util.TreeSet;

    class Employee {
    String empId;
    String groupId;
    int salary;

    public Employee(String empId, String groupId, int salary) {
    this.empId = empId;
    this.groupId = groupId;
    this.salary = salary;
    }

    public String getEmpId() {
    return empId;
    }

    public String getGroupId() {
    return groupId;
    }

    public int getSalary() {
    return salary;
    }

    @Override
    public boolean equals(Object obj) {
    Employee other = (Employee) obj;
    return Objects.equals(empId, other.empId) && Objects.equals(groupId, other.groupId)
    && Objects.equals(salary, other.salary);
    }

    @Override
    public String toString() {
    return "Employee [empId=" + empId + ", groupId=" + groupId + ", salary=" + salary + "]";
    }
    }

    class EmployeeChainedComparator implements Comparator<Employee> {

    private List<Comparator<Employee>> listComparators;

    public EmployeeChainedComparator(Comparator<Employee>... comparators) {
    this.listComparators = Arrays.asList(comparators);
    }

    @Override
    public int compare(Employee o1, Employee o2) {
    for (Comparator<Employee> comparator : listComparators) {
    int result = comparator.compare(o1, o2);
    if (result != 0)
    return result;
    }

    return 0;
    }

    }

    class EmployeeGroupComparator implements Comparator<Employee> {

    @Override
    public int compare(Employee o1, Employee o2) {
    if (o2.getGroupId() == null)
    return (o1.getGroupId() == null) ? 0 : -1;
    if (o1.getGroupId() == null)
    return 1;
    return o1.getGroupId().compareTo(o2.getGroupId());
    }

    }

    class EmployeeSalaryComparator implements Comparator<Employee> {

    @Override
    public int compare(Employee o1, Employee o2) {
    return o2.getSalary() - o1.getSalary();
    }

    }

    class Solution {
    void sortEmployees(List<Employee> employees) {
    Collections.sort(employees,
    new EmployeeChainedComparator(new EmployeeGroupComparator(), new EmployeeSalaryComparator()));
    }
    }

    public class Q62447064 {
    public static void main(String[] args) {
    List<Employee> empList = new ArrayList<>(List.of(new Employee("emp1", "grp1", 500),
    new Employee("emp2", null, 600), new Employee("emp3", null, 700), new Employee("emp4", "grp2", 800),
    new Employee("emp5", "grp1", 700), new Employee("emp6", "grp2", 1000),
    new Employee("emp7", "grp1", 800), new Employee("emp8", null, 1000),
    new Employee("emp9", "grp2", 600)));

    // Sort employees using the comparators defined in the class, Solution
    new Solution().sortEmployees(empList);

    // Group employees by group ID with the sum of salary as the grouping function
    Map<String, Integer> map = new HashMap<>();
    for (Employee e : empList) {
    String grp = e.getGroupId();
    if (grp == null) {
    grp = "null";
    }
    Integer salary = map.get(grp);
    map.put(grp, salary == null ? e.getSalary() : e.getSalary() + salary);
    }

    // Result list
    List<Employee> result = new ArrayList<>();

    // Iterate the sorted entry set of `map` and put the records corresponding to
    // an entry into the result list
    for (Entry<String, Integer> entry : entriesSortedByValues(map)) {
    String grp = entry.getKey();
    int i;

    // Find the starting index of `grp` in empList

    if ("null".equals(grp)) {// Special handling for employees with `null` group
    // Find the index in `empList` where employees with the group as `null` starts
    for (i = 0; i < empList.size() && empList.get(i).getGroupId() != null; i++)
    ;

    // Add elements before a different group is encountered
    for (int j = i; j < empList.size() && empList.get(j).getGroupId() == null; j++) {
    result.add(empList.get(j));
    }
    } else {
    // Find the index in `empList` where employees with the group as `grp` starts
    for (i = 0; i < empList.size() && !grp.equals(empList.get(i).getGroupId()); i++)
    ;

    // Add elements before a different group is encountered
    for (int j = i; j < empList.size() && grp.equals(empList.get(j).getGroupId()); j++) {
    result.add(empList.get(j));
    }
    }
    }

    // Display result list
    for (Employee e : result) {
    System.out.println(e);
    }
    }

    private static <K, V extends Comparable<? super V>> SortedSet<Map.Entry<K, V>> entriesSortedByValues(
    Map<K, V> map) {
    SortedSet<Map.Entry<K, V>> sortedEntries = new TreeSet<Map.Entry<K, V>>(new Comparator<Map.Entry<K, V>>() {
    @Override
    public int compare(Map.Entry<K, V> e1, Map.Entry<K, V> e2) {
    int res = e2.getValue().compareTo(e1.getValue());
    return res != 0 ? res : 1;
    }
    });
    sortedEntries.addAll(map.entrySet());
    return sortedEntries;
    }
    }

    Output:

    Employee [empId=emp6, groupId=grp2, salary=1000]
    Employee [empId=emp4, groupId=grp2, salary=800]
    Employee [empId=emp9, groupId=grp2, salary=600]
    Employee [empId=emp8, groupId=null, salary=1000]
    Employee [empId=emp3, groupId=null, salary=700]
    Employee [empId=emp2, groupId=null, salary=600]
    Employee [empId=emp7, groupId=grp1, salary=800]
    Employee [empId=emp5, groupId=grp1, salary=700]
    Employee [empId=emp1, groupId=grp1, salary=500]

    Note: The method, entriesSortedByValues has been copied from this post.

How to sort list based on multiple fields value in java

The thenComparing is for nested sorting: when there are objects with equal values, they are then internally sorted by the next property.

What you apparently want is to define your own completely separate sort order:

list.sort(Comparator.comparing(o -> {
if (Boolean.TRUE.equals(o.getPassport()) {
return 1;
} else if (Boolean.FALSE.equals(o.getFeatured()) {
return 2;
} else if ("Cancelled".equals(o.getStatus()) {
return 3;
} else if ("Unconfirmed".equals(o.getStatus()) {
return 4;
} else {
return 5;
}
}));

Sort collection by multiple fields in Kotlin

sortedWith + compareBy (taking a vararg of lambdas) do the trick:

val sortedList = list.sortedWith(compareBy({ it.age }, { it.name }))

You can also use the somewhat more succinct callable reference syntax:

val sortedList = list.sortedWith(compareBy(Person::age, Person::name))


Related Topics



Leave a reply



Submit