I have a list of records, where each record has two primary keys Primary Id and Alternate Id. I want to make a map through which I can access the processed records using either Primary Id or the Alternate Id using RxJava operations.
Current implementation:
ImmutableMap.Builder<String, Record> mapBuilder = new ImmutableMap.Builder<>();
fetchRecords()
.forEach(
record -> {
parsedRecord = dosomething(record);
mapBuilder.put(parsedRecord.getPrimaryId(), parsedRecord);
mapBuilder.put(parsedRecord.getAlternativeId(), parsedRecord);
});
return mapBuilder.build();
How I want it to look like:
fetchRecords().stream()
.map(doSomething)
.collect(Collectors.toMap(RecordData::getPrimaryId, Function.identity()));
// Need to add this as well
.collect(Collectors.toMap(RecordData::getAlterantiveId, Function.identity()));
Just wanted to know if there is a way that I can add the secondary id to record mapping as well in a single pass over fetchRecords().
I'm not familiar with rx-java but this might provide a starting point. Create a custom collector to add both keys. This is a very basic collector and does not handle duplicate keys other than using the last one provided. I simply used the new record
feature introduced in Java 15 to create an immutable "class"
. This would work the same way for a regular class.
record ParsedRecord(String getPrimaryId,
String getAlternativeId, String someValue) {
@Override
public String toString() {
return someValue;
}
}
Map<String, ParsedRecord> map = records.stream()
.collect(twoKeys(ParsedRecord::getPrimaryId,
ParsedRecord::getAlternativeId, p -> p));
map.entrySet().forEach(System.out::println);
Prints the following:
A=value1
B=value1
C=value2
D=value2
Here is the collector. It is essentially a simplified version of toMap
that takes two keys instead of one.
private static <T, K, V> Collector<T, ?, Map<K, V>> twoKeys(
Function<T, K> keyMapper1, Function<T, K> keyMapper2,
Function<T, V> valueMapper) {
return Collector.of(
() -> new HashMap<K, V>(),
(m, r) -> {
V v = valueMapper.apply(r);
m.put(keyMapper1.apply(r),v);
m.put(keyMapper2.apply(r),v);
}, (m1, m2) -> {
m1.putAll(m2);
return m1;
}, Characteristics.UNORDERED);
}
Or just keep it simple and efficient.
Map<String, ParsedRecord> map = new HashMap<>();
for(ParsedRecord pr : records) {
map.put(pr.getPrimaryId(), pr);
map.put(pr.getAlternativeId(), pr);
}