I checked out some related questions but none answered my question. I'm trying to sort a Map ascending alphabetically, but am having issues. Here is the code currently:
// provinceName is key
public Map<String, String> regionMap() {
return
em
.createQuery(
"""
SELECT DISTINCT p.provinceName AS prov_id ,p.provinceAbbreviation AS prov_value
FROM CanadianPersonalIncomeTaxRate p
ORDER BY p.provinceName ASC
""",
Tuple.class
)
.getResultStream()
// .filter(prov_value -> prov_value.get())
// .sorted(Comparator.comparing(prov_value -> prov_value))
.collect(
Collectors.toMap(
tuple -> tuple.get(0, String.class),
tuple -> tuple.get(1, String.class)
)
);
}
I was thinking about using .sorted or TreeMap, but am open to suggestions on how I can do it the cleanest/simply. Any help is greatly appreciated.
TreeMap :: new
Change the declaration of your result to indicate you want a map sorted by key. Use NavigableMap
.
This:
public Map<String, String> regionMap() {
… should be:
public NavigableMap<String, String> regionMap() {
Specify a concrete implementation of NavigableMap
by passing another argument, a map factory such as TreeMap :: new
, to your Collectors.toMap
call. We must also pass a merge function to decide what to do in case of duplicate elements. See the Javadoc for this overload of Collectors.toMap
.
Collectors.toMap(
tuple -> tuple.get( 0 , String.class ) ,
tuple -> tuple.get( 1 , String.class ) ,
( oldValue , newValue ) -> oldValue ) ,
TreeMap :: new
)
Your database sorting becomes superfluous in this approach. Since you are sorting on the Java side, you could delete the line of SQL:
ORDER BY p.provinceName ASC
But then again, adding elements to the TreeMap
in pre-sorted order might help performance of the map’s sorting (just a guess on my part). So I would consider keeping the ORDER BY
.
If you want to return an unmodifiable map, use Collectors.toUnmodifiableMap
with same arguments.
Here is some example code.
First, some input data, to simulate your database query.
String[][] inputs =
{
{ "Ontario" , "ON" } ,
{ "Québec" , "QA" } ,
{ "Nova Scotia" , "NS" } ,
{ "New Brunswick" , "NB" } ,
{ "Manatob" , "MB" } ,
{ "British Columbia" , "BC" } ,
{ "Prince Edward Island" , "PE" } ,
{ "Saskatchewan" , "SK" } ,
{ "Alberta" , "AB" } ,
{ "Newfoundland and Labrador" , "NL" }
};
And the logic. We retrieve each row from the array of arrays. We extract the first and second element from each row/array by zero-based index counting (0
& 1
). And we collect those into a NavigableMap
, specifically a TreeMap
.
NavigableMap < String, String > map =
Arrays
.stream( inputs )
.collect(
Collectors.toMap(
( String[] input ) -> input[ 0 ] ,
( String[] input ) -> input[ 1 ] ,
( String oldValue , String newValue ) -> oldValue ,
TreeMap :: new
)
);
Or, if you don't need to see the type declarations, use the following. Your situation is complicated enough that I would personally retain the type declarations for clarity. Doing so would have prevented the error you made in naming oldTuple , newTuple
when really it is the String
value objects that are being merged, oldValue, newValue
.
NavigableMap < String, String > map =
Arrays
.stream( inputs )
.collect(
Collectors.toMap(
input -> input[ 0 ] ,
input -> input[ 1 ] ,
( oldValue , newValue ) -> oldValue ,
TreeMap :: new
)
);
Result:
map.toString() = {Alberta=AB, British Columbia=BC, Manatob=MB, New Brunswick=NB, Newfoundland and Labrador=NL, Nova Scotia=NS, Ontario=ON, Prince Edward Island=PE, Québec=QA, Saskatchewan=SK}
We can skip over unwanted rows.
Stream :: filter
.Predicate
. Notice the !
in our predicate. We are filtering for elements to keep, not lose.String[][] inputs =
{
{ "Ontario" , "ON" } ,
{ "Federal" , "CAN" } , // <-- Unwanted row.
{ "Québec" , "QA" } ,
{ "Nova Scotia" , "NS" } ,
{ "New Brunswick" , "NB" } ,
{ "Manatob" , "MB" } ,
{ "British Columbia" , "BC" } ,
{ "Prince Edward Island" , "PE" } ,
{ "Saskatchewan" , "SK" } ,
{ "Alberta" , "AB" } ,
{ "Newfoundland and Labrador" , "NL" }
};
NavigableMap < String, String > map =
Arrays
.stream( inputs )
.filter( ( String[] input ) -> ! input[ 1 ].equalsIgnoreCase( "CAN" ) )
.collect(
Collectors.toMap(
input -> input[ 0 ] ,
input -> input[ 1 ] ,
( oldValue , newValue ) -> oldValue ,
TreeMap :: new
)
);