Sorting Java Objects Using Multiple Keys

Sorting Java objects using multiple keys

Java 8 solution:

Comparator<Duck> cmp = Comparator.comparing(Duck::getWeight)
.thenComparing(Duck::getAge)
.thenComparing(Duck::getName);

Hooray for lambdas, method references, and default methods:)! Too bad we have to define getters, or use explicit lambdas, like so:

Comparator<Duck> cmp = Comparator
.comparing((Duck duck)-> duck.weight)
.thenComparing((Duck duck)-> duck.age)
.thenComparing(duck-> duck.name);

Type inference won't work with implicit lambdas, so you have to specify the argument type of the first two lambdas. More details in this answer by Brian Goetz.

Sort list of maps using multiple keys in Java8

Since Java8, the Comparator interface offers factory methods and chaining methods:

Comparator<Map<String, String>> c
= Comparator.comparing((Map<String, String> m) -> m.get(Key1))
.thenComparing(m -> m.get(Key2))
.thenComparing(m -> m.get(Key3))
.thenComparing(m -> m.get(Key4));

listOfMaps.sort(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 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;
}

Sort list of map objects using multiple boolean keys in Java8

I have tried the similar logic that you applied for java script.
You can try with the following code in java 8:

public class ListMapSort {

public static void main(String[] args) {
List<Map<String, Object>> data = getData();

data.sort(Comparator.comparing(m -> {
int lastKey = (m.containsKey("last") && (boolean)m.get("last")) ? 1 : 0;
int upKey = (m.containsKey("up") && (boolean)m.get("up")) ? 0 : 1;
return lastKey*10 + upKey;
}));

System.out.println("Sorted data: " + data);
}

private static List<Map<String, Object>> getData() {
List<Map<String, Object>> data = new ArrayList<>();
Map<String, Object> map = new HashMap<>();
map.put("up", false);
map.put("id", 1);
data.add(map);
map = new HashMap<>();
map.put("up", false);
map.put("id", 2);
data.add(map);
map = new HashMap<>();
map.put("last", false);
map.put("up", false);
map.put("id", 3);
data.add(map);
map = new HashMap<>();
map.put("last", true);
map.put("up", true);
map.put("id", 4);
data.add(map);
map = new HashMap<>();
map.put("up", true);
map.put("id", 5);
data.add(map);
map = new HashMap<>();
map.put("last", false);
map.put("id", 6);
data.add(map);
map = new HashMap<>();
map.put("last", true);
map.put("id", 7);
data.add(map);
map = new HashMap<>();
map.put("last", false);
map.put("up", true);
map.put("id", 7);
data.add(map);

return data;
}
}

As it has been said in the other post mentioned by you, it can be solved using this way as well,

data.sort(Comparator.comparing(
(Map<String, Object> m) -> (boolean)m.getOrDefault("last", false))
.thenComparing(m -> !(boolean)m.getOrDefault("up", false)));

Sorting by multiple keys in nested HashMap

Usually in situations like this the job of the Comparator is to simple return the value of a compare from something else. For example, here is a Comparator that will alphabetize Fonts:

class FontAlphabetizer
implements Comparator<Font> {
@Override
public int compare(Font font1, Font font2) {
return font1.getName().compareTo(font2.getName());
}
}

That's actually pretty simple: getName returns a String and all we do is return the value of String's compareTo method.

Here it seems like what you have is an ArrayList<Map> and you want to sort the ArrayList based on a chosen value from the Map. So what you need is a Comparator<Map>. And you need to give the Comparator the key for the corresponding value that you want to sort by. This can be expressed generically like the following:

class MapValueComparator<K, V extends Comparable<V>>
implements Comparator<Map<K, V>> {
final K key;

MapValueComparator(K key) {
this.key = key;
}

@Override
public int compare(Map<K, V> map1, Map<K, V> map2) {
return map1.get(key).compareTo(map2.get(key));
}
}

That is a Comparator that compares Maps and it's specified in the declaration there that the Map's values must also be Comparable. It compares based on the value retrieved from the given key.

So for example if we have an ArrayList<Map<String, String>>, we can sort by the value from "town" like this:

static void sortByTown(List<Map<String, String>> list) {
Collections.sort(list, new MapValueComparator<String, String>("town"));
}

The hiccup is that you say you have town=Toronto, population=2,500,000 which indicates that the population you want to sort by is a String (since presumably it's in the same map as Toronto). Comparing population as String probably isn't desired because it will sort lexicographically (50 comes after 2,500,000 because 5 comes after 2). In that case the generic version might not work because you need to take an extra step of converting the value to a number.

class PopulationComparator
implements Comparator<Map<String, String>> {
@Override
public int compare(Map<String, String> map1, Map<String, String> map2) {
final Long pop1 = Long.valueOf(map1.get("population"));
final Long pop2 = Long.valueOf(map2.get("population"));

return pop1.compareTo(pop2);
}
}

(And as a side note if your population contains commas you'd need to format that before parsing it to a number. You can use replaceAll("\\D", "") to remove all non digits from a String.)

This is also a case where it could be advantageous to create a class for this instead of using a Map. Then you could have the numerical fields be number types. If you had a class, the comparison would be mostly the same though: just returning a comparison of a chosen field.

JAVA - Sort Array of Json by multiple values while preserving previous sort order

A sorting algorithm that preserves the existing order on same-valued entries is called stable. In Java, you can consult the API whether a given sorting function is guaranteed to be stable, such as Arrays.sort.

The typical way of sorting using multiple keys is to sort the entries sequentially with a stable sorting algorithm in reverse order of keys.

For example, if you want to order by first name first and last name second, you would first sort by last name and then by first name.

You also need to make sure that the data structure you use preserves the order of insertion, for example a Set or Map may not preserve that.

Sort list of maps based on multiple keys

You can use Dataweave to achieve this

%dw 1.0
%output application/json
---
payload orderBy ($.date ++ $.publication ++ $.boNumber)

Hope this helps.

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

how sort in java8 list of lists of object by multiple properties

Just use comparator, an example of sorting by firstName then by lastName

list.forEach(l -> l.sort(Comparator.comparing(CsvEntity::getFirstName)
.thenComparing(CsvEntity::getLastName)));

Of course you have to have getters for your fields to use method reference



Related Topics



Leave a reply



Submit