Map<String, Integer> nonOrderedData = // {b=1, c=2, d=3, e=4, a=0}
List<String> orderSequence = // a, b, c, d, e
I need to apply the order sequence to get correctly ordered data, how could I achieve this using (prefered) stream?
What I used is non stream way:
Map<String, Integer> orderedData = new HashMap<>();
for (Map.Entry<String, Integer> nod : nonOrderedData.entrySet()) {
for (String os : orderSequence) {
if (os == nod.getKey()) {
// add nonOrderedData data
} else {
// add data by sequence
}
}
}
Would like to have more cleaner way of what I want.
I've noticed that in my method I could just return new TreeMap<>(nonOrderedData)
and it would work just fine, but I don't want to stick to just applying asc order - I would like to read actual sequence values and then change nonOrderedData
.
The HashMap
cannot be used to store orderedData
because it does not guarantee ordering of its keys, so LinkedHashMap
should be used which maintains insertion order.
Ordering by the data presented in orderSequence
using streams may be implemented in two modes:
nonOrderedData
:Map<String, Integer> nonOrderedData = Map.of(
"b", 1, "e", 4, "a", 0, "o", 5, "d", 7
);
List<String> orderSequence = Arrays.asList(
"a", "e", "i", "o", "u", "b", "c", "d"
);
Map<String, Integer> reordered = orderSequence
.stream()
.filter(nonOrderedData::containsKey)
.collect(Collectors.toMap(
key -> key, nonOrderedData::get,
(v1, v2) -> v1, LinkedHashMap::new
));
System.out.println(reordered);
Output:
{a=0, e=4, o=5, b=1, d=7}
It is similar to INNER JOIN
between orderSequence
and nonOrderedData
.
reorderedData
with some default value if the key from orderSequence
is missing in nonOrderedData
:Map<String, Integer> reorderedWithDefault = orderSequence
.stream()
.collect(Collectors.toMap(
key -> key, nonOrderedData.getOrDefault(key, -1),
(v1, v2) -> v1, LinkedHashMap::new
));
System.out.println(reorderedWithDefault);
Output:
{a=0, e=4, i=-1, o=5, u=-1, b=1, c=-1, d=7}
It is similar to LEFT JOIN
between orderSequence
and nonOrderedData
.
Update
In the above-mentioned implementations the key-value pairs in nonOrderedData
that do not match to the keys in orderSequence
are skipped altogether. Such keys may be tracked (and added later to the reordered
result) using Map::remove (Object key)
which returns a value of the key being removed.
However, the following two code examples modify the state of nonOrderedData
outside the stream execution.
nonOrderedData
, place the non-matched pairs to the end:Map<String, Integer> nonOrderedData = new HashMap<>(Map.of(
"b", 1, "e", 4, "z", 8, "a", 0, "q", 6,
"f", 5, "d", 7
));
List<String> orderSequence = Arrays.asList("a", "e", "i", "o", "u", "b", "c", "d");
Map<String, Integer> reordered = orderSequence
.stream()
.filter(nonOrderedData::containsKey)
.collect(Collectors.toMap(
key -> key, nonOrderedData::remove,
(v1, v2) -> v1, LinkedHashMap::new
));
SortedMap<String, Integer> remainder = new TreeMap<>(nonOrderedData);
System.out.println("remained: " + remainder);
reordered.putAll(remainder);
System.out.println(reordered);
Output:
remained: {f=5, q=6, z=8}
{a=0, e=4, b=1, d=7, f=5, q=6, z=8}
It is similar to RIGHT JOIN
between orderSequence
and nonOrderedData
.
orderSequence
and nonOrderedData
similar to FULL JOIN
Here default values will be provided for the non-mapped keys in orderSequence
and non-matched keys from nonOrderedData
will be added to the end.
Map<String, Integer> reorderedFull = orderSequence
.stream()
.peek(key -> nonOrderedData.computeIfAbsent(key, (k) -> -1)) // default value
.collect(Collectors.toMap(
key -> key, nonOrderedData::remove,
(v1, v2) -> v1, LinkedHashMap::new
));
SortedMap<String, Integer> remainderFull = new TreeMap<>(nonOrderedData);
System.out.println("remained: " + remainderFull);
reorderedFull.putAll(remainderFull);
System.out.println(reorderedFull);
Output:
remained: {f=5, q=6, z=8}
{a=0, e=4, i=-1, o=-1, u=-1, b=1, c=-1, d=7, f=5, q=6, z=8}