Sorting a List of Map<String, String>

sorting a List of MapString, String

The following code works perfectly

public Comparator<Map<String, String>> mapComparator = new Comparator<Map<String, String>>() {
public int compare(Map<String, String> m1, Map<String, String> m2) {
return m1.get("name").compareTo(m2.get("name"));
}
}

Collections.sort(list, mapComparator);

But your maps should probably be instances of a specific class.

Sort ListMapString,Object based on value

You can generalize comparing numbers by comparing their double values, because they are the largest. If two objects cannot be cast to numbers and the double value cannot be parsed from these objects, then compare their string values:

List<Map<String, Object>> data = Arrays.asList(
Map.of("Field1", 21.2d), // Double
Map.of("Field1", "qqq"), // String
Map.of("Field1", "22.5"), // String
Map.of("Field1", 2), // Integer
Map.of("Field1", 3L), // Long
Map.of("Field1", 23.1f)); // Float
data.sort(Comparator.comparingDouble((Map<String, Object> map) -> {
Object object = map.get("Field1");
if (object instanceof Number) {
return ((Number) object).doubleValue();
} else {
try {
return Double.parseDouble(String.valueOf(object));
} catch (NumberFormatException e) {
return Double.NaN;
}
}
}).thenComparing(map -> String.valueOf(map.get("Field1"))));
data.forEach(System.out::println);
// {Field1=2}
// {Field1=3}
// {Field1=21.2}
// {Field1=22.5}
// {Field1=23.1}
// {Field1=qqq}

See also: Sort 2D List by Column Header

How can I sort a MapString, ListCustomObject?

By changing the type of Map used in my code above, as suggested by @Thomas, in TreeMap<>, I found the solution to the problem as follows:

  1. I first merged all the Person object lists into one list. This was then sorted by the chosen criterion, for example Person.COMPARE_BY_NAME;
  2. I created an algorithm that would re-group the sorted lists, in according to the criteria of my project, in a map. The key of this map corresponds to the concatenation of the month + year of the Person object.
    The algorithm is reported at the bottom of the comment;
  3. I sort the map based on the chosen attribute, for example Sorter.COMPARATOR_BY_NAME;

Di seguito il codice è come segue:

Merge all List<Person> in one -> main or somewhere before the Map was created

    ...
//
List<Person> newPersonList = new ArrayList<>();
newPersonList.addAll(oldPersonList1);
newPersonList.addAll(oldPersonList2);
...

Main or somewhere before the Map was created

    ...
groupList(Person.COMPARE_BY_NAME, Sorter.COMPARATOR_BY_NAME);
...

GroupPerson -> method to group the merged List<Person> in a TreeMap<String, List<Person>>

    public Map<String, List<Person>> groupList(final Comparator<? super Person> itemComparator, final Comparator<? super List<Person>> listComparator)

// Sort Person list by comparator before create TreeSet
newPersonList.sort(itemComparator);

Map<String, List<Person>> personMapGrouped = new TreeMap<>();

// Here, create a Map of list
for (Person person: newPersonList) {
final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy MM", Locale.getDefault());
final String groupKey = dateFormat.format(person.getDateOfBirth());

if (personMapGrouped.containsKey(groupKey)) {
// The key is already in the TreeMap; add the Person object against the existing key.
final List<Person> personListGrouped = personMapGrouped.get(groupKey);
if (personListGrouped!= null) {
personListGrouped.add(person);
}
} else {
// The key is not there in the TreeMap; create a new key-value pair
final List<Person> personListGrouped = new ArrayList<>();
personListGrouped.add(person);
personMapGrouped.put(groupKey, personListGrouped);
}
}
// Here sort the Map by params passed
final TabPersonSorter sorter = new TabPersonSorter();
personMapGrouped = sorter.sort(personMapGrouped, listComparator);
}

In this case, using the lists created in the main above, the results obtained are:

    "List<Person> mergedList": [
Person("name1", new Date("2022-01-01")),
Person("name3", new Date("2021-02-05")),
Person("name4", new Date("2021-02-03")),
Person("name12", new Date("2022-01-05")),
Person("name13", new Date("2022-01-03")),
Person("name14", new Date("2021-02-01"))
]

"Map<String, List<Person>> mergedMap": {
"2022-01": [
Person("name1", new Date("2022-01-01")),
Person("name12", new Date("2022-01-05")),
Person("name13", new Date("2022-01-03"))
],
"2021-02": [
Person("name3", new Date("2021-02-05")),
Person("name4", new Date("2021-02-03"))
],
"2022-02": [
Person("name14", new Date("2021-02-01"))
]
}

Obviously if the grouping in the map were not bound by such a restrictive date as only year + month, the sorting would have the desired effect in distinct groups.
In fact, in the case of the sorting by date, this is respected very well.

Sorting a list of MapString, String based on a particular numeric value

If is not possible to create a class from that map then you can do something like:

Collections.sort(foo, (o1, o2) -> {
return new BigDecimal(o2.get("score")).compareTo(new BigDecimal(o1.get("score")));
});

or if you are not using java 8:

Collections.sort(foo, new Comparator<Map<String, String>>() {
@Override
public int compare(Map<String, String> o1, Map<String, String> o2) {
return new BigDecimal(o2.get("score")).compareTo(new BigDecimal(o1.get("score")));
}
});

How to sort ListMapString,String by key1 descending order and key2 ascending order if key1 has same values

It's easier if you build your Comparator in two steps. So try this and print them out. It works like this.

  • first it sorts the price in reversed order.
  • Then t sorts the product number in regular order for equal prices.
    Comparator<Map<String, String>> comp = Comparator
.comparing(m ->Double.parseDouble(m.get("price"))
,Comparator.reverseOrder());
comp = comp.thenComparing(m -> m.get("productNumber"));

Apply it like so.

        List<Map<String, String>> formattedResult =
list.stream().sorted(comp)
.collect(Collectors.toList());

formattedResult.forEach(m -> System.out.println(
m.get("price") + " : " + m.get("productNumber")));

This prints

1.99 : 107-001
1.99 : 109-001
1.02 : 108-001

Sort a list of maps based on two values

You could implement a custom Comparator based on those two values:

list.sort(Comparator.comparing((Map<String, Object> map) -> (Integer) map.get("Age"))
.thenComparing((Map<String, Object> map) -> (String) map.get("Name"))
);

Sort ListMapString, Object by a particular key in the map

Since JAVA 1.8 there is new method in java.util.Comparator called comparing.
It returns comparator comparing objects by given key.
https://docs.oracle.com/javase/8/docs/api/java/util/Comparator.html#comparing-java.util.function.Function-

In this case to compare by names it would look like:

 list.sort(Comparator.comparing(o -> String.valueOf(o.get("name"))));

and to compare by id:

 list.sort(Comparator.comparing(o -> (Long) o.get("id")));

How to i sort a ListMapString,Object in java

You can use a custom comparator, but you need your values (currently Object) to implement Comparable. Based on the current declaration of your list, you can't do it (how do you compare two random objects?):

List<Map<String,Comparable>> listMap = ...
Collections.sort(listMap, new Comparator<Map<String, Comparable>> () {

@Override
public int compare(Map<String, Comparable> m1, Map<String, Comparable> m2) {
return m2.get("full_inc").compareTo(m1.get("full_inc")); //descending
}
});

Note: you need to add error checking.

Sort the values in a map of type MapString, ListString

Since list you want to sort can contain null and you can't call compareTo on null (since it doesn't have any methods nor fields) you will need to provide your own Comparator which will handle null and use it with sorting method.

For instance if you would like to place null at end of ascending order you will need to implement rules like:

  • null null - don't swap, there is no point (return 0)
  • null "someString" - swap, null is bigger and should be placed after "someString" (return 1)
  • "someString" null - don't swap, first argument ("someString") is smaller than null (return -1)
  • "string1" "string2" - return default result of comparing both non-null values

and your Comparator can look like

Comparator<String> myComparator = new Comparator<String>() {
@Override
public int compare(String s1, String s2) {
if (s1==null && s2==null) return 0;//swapping has no point here
if (s1==null) return 1;
if (s2==null) return -1;
return s1.compareTo(s2);
}
};

Now you can use it

for (List<String> list : yourMap.values()) {
Collections.sort(list, myComparator);
}


Java 8 update

Since Java 8 Comparator provides methods which can wrap other comparator and create another one which will place nulls at start or end of our collection. This methods are:

  • Comparator.nullsFirst(Comparator)
  • Comparator.nullsLast(Comparator)

Also sort(Comparator) method was added to List interface which means we don't need to call explicitly Collections.sort(list,comparator).

So your code can look like:

for (List<String> list : yourMap.values()) {
list.sort(Comparator.nullsLast(Comparator.naturalOrder()));
}

But nothing stops you from using other comparator instead of Comparator.naturalOrder() like one stored in String.CASE_INSENSITIVE_ORDER which now can also be created with String::compareToIgnoreCase method reference.



Related Topics



Leave a reply



Submit