Java flatten nested Maps and concatenate keys - java

I have a HashMap as below:
Map<String,Object> map = new HashMap<>();
Map<String,Object> map1 = new HashMap<>();
map1.put("key1", "value1");
Map<String,Object> map2 = new HashMap<>();
Map<String,Object> map3 = new HashMap<>();
map3.put("key2", "value2");
map2.put("map3", map3);
map.put("map1", map1);
map.put("map2", map2);
map.put("key3", "value3");
I want to flatten it. Expected output is:
[map1.key1, value1]
[map2.map3.key2, value2]
[key3, value3]
...
This can be done using simple for loops with following code:
public static Map<String, String> flat(Map<String, Object> input){
Map<String, String> toReturn = new HashMap<>();
for (Map.Entry<String, Object> entry: input.entrySet()) {
if(entry.getValue() instanceof Map){
Map<String, Object> innerMap = (Map<String, Object>)entry.getValue();
for(Map.Entry<String, Object> innerEntry: innerMap.entrySet()) {
if(innerEntry.getValue() instanceof Map){
...
...
}
else {
toReturn.put(entry.getKey() + "." + innerEntry.getKey(), innerEntry.getValue().toString());
}
}
} else {
toReturn.put(entry.getKey(), entry.getValue().toString());
}
}
return toReturn;
}
Code to do this recursively:
public static Map<String, String> flat(Map<String, Object> input){
Map<String, String> toReturn = new HashMap<>();
rec(toReturn, input, new ArrayList<>());
return toReturn;
}
public static void rec(Map<String, String> toReturn, Map<String, Object> input, List<String> keys) {
for (Map.Entry<String, Object> entry: input.entrySet()) {
if(entry.getValue() instanceof Map){
keys.add(entry.getKey());
rec(toReturn, (Map<String, Object>) entry.getValue(), keys);
} else {
final StringBuffer key = new StringBuffer();
if(keys.size() > 0) {
keys.forEach(x -> key.append(x).append("."));
}
key.append(entry.getKey());
toReturn.put(key.toString(), entry.getValue().toString());
}
}
if(keys.size() > 0) {
keys.remove(keys.size() - 1);
}
}
How do I achieve this using Java Stream API ?

This is generally the same solution as in the mentioned link but with some update to work with entrySet() and add a key from containing map as a prefix:
public class FlattenMap {
public static Stream<Map.Entry<String, Object>> flatten(Map.Entry<String, Object> entry) {
if (entry.getValue() instanceof Map<?, ?>) {
Map<String, Object> nested = (Map<String, Object>) entry.getValue();
return nested.entrySet().stream()
.map(e -> new AbstractMap.SimpleEntry(entry.getKey() + "." + e.getKey(), e.getValue()))
.flatMap(FlattenMap::flatten);
}
return Stream.of(entry);
}
public static void main(String[] args) {
Map<String,Object> map = new HashMap<>();
Map<String,Object> map1 = new HashMap<>();
map1.put("key1", "value1");
Map<String,Object> map2 = new HashMap<>();
Map<String,Object> map3 = new HashMap<>();
map3.put("key2", "value2");
map2.put("map3", map3);
map.put("map1", map1);
map.put("map2", map2);
map.put("key3", "value3");
// collecting to List of entries
map.entrySet().stream()
.flatMap(FlattenMap::flatten)
.collect(Collectors.toList())
.forEach(System.out::println);
// collecting entries back to flattened map
Map<String, Object> remapped = map.entrySet().stream()
.flatMap(FlattenMap::flatten)
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
remapped.entrySet().stream()
.forEach(e -> System.out.printf("[%s, %s]%n", e.getKey(), e.getValue()));
}
}
It prints results:
map2.map3.key2=value2
map1.key1=value1
key3=value3
[key3, value3]
[map2.map3.key2, value2]
[map1.key1, value1]

Related

Group and Aggregate List of Map<String, Object>

I have a List<Map<String, Object>> input like below:
[{
CURRENCY = USD,
STATUS = NEW,
PUBLISH_REGION = DEL,
SOURCE = ALADDIN,
RECON_STATUS = null,
JOB_ID_COUNT = 783
}, {
CURRENCY = USD,
STATUS = IN_PROGRESS,
PUBLISH_REGION = DEL,
SOURCE = ALADDIN,
RECON_STATUS = null,
JOB_ID_COUNT = 462
}, {
CURRENCY = USD,
STATUS = NEW,
PUBLISH_REGION = DEL,
SOURCE = GROUP,
RECON_STATUS = null,
JOB_ID_COUNT = 4
}]
I am trying to create another List<Map<String, Object>> by grouping on CURRENCY, PUBLISH_REGION, SOURCE and RECON_STATUS columns. And add all unique STATUS values as pivot to the output map and use JOB_ID_COUNT to summarize/aggregate the count.
List<String> groups = new ArrayList<>(asList("SOURCE", "RECON_STATUS", "PUBLISH_REGION", "CURRENCY"));
List<Map<String, Object>> = input.stream()
.collect(groupingBy(row -> row.get(groups.get(0)), mapping(map -> map.get(groups.get(0)), toList())));
I am expecting below response:
Output:
[{
CURRENCY = USD,
PUBLISH_REGION = DEL,
SOURCE = ALADDIN,
RECON_STATUS = null,
NEW = 783,
IN_PROGRESS = 462
}, {
CURRENCY = USD,
PUBLISH_REGION = DEL,
SOURCE = GROUP,
RECON_STATUS = null,
NEW = 4,
IN_PROGRESS = 0
}]
I am getting compile time error when trying to group by multiple map fields. Single field groupingBy is working fine. Any help is greatly appriciated.
Without Using Custom Class
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class MultipleFieldSorting2 {
private static Map<String, Object> map, map1, map2;
private static List<Map<String, Object>> lst = new ArrayList<>();
static {
map = new HashMap<>();
map.put("CURRENCY", "USD");
map.put("STATUS", "NEW");
map.put("PUBLISH_REGION", "DEL");
map.put("SOURCE", "ALADDIN");
map.put("RECON_STATUS", null);
map.put("JOB_ID_COUNT", "783");
map1 = new HashMap<>();
map1.put("CURRENCY", "USD");
map1.put("STATUS", "IN_PROGRESS");
map1.put("PUBLISH_REGION", "DEL");
map1.put("SOURCE", "ALADDIN");
map1.put("RECON_STATUS", null);
map1.put("JOB_ID_COUNT", "462");
map2 = new HashMap<>();
map2.put("CURRENCY", "USD");
map2.put("STATUS", "NEW");
map2.put("PUBLISH_REGION", "DEL");
map2.put("SOURCE", "GROUP");
map2.put("RECON_STATUS", null);
map2.put("JOB_ID_COUNT", "4");
lst.add(map);
lst.add(map1);
lst.add(map2);
}
public static Map<String, Object> mapper(Map<String, Object> e){
String key = e.get("CURRENCY") + "-" + e.get("PUBLISH_REGION") + "-" + e.get("SOURCE") + "-" + e.get("RECON_STATUS");
Map<String, Object> groupedValue = res.get(key);
if(groupedValue!=null){
groupedValue.put((String) e.get("STATUS"), groupedValue.get("STATUS")!=null ? groupedValue.get("STATUS")+","+e.get("JOB_ID_COUNT") : e.get("JOB_ID_COUNT"));
if(groupedValue.get("NEW")==null){
groupedValue.put("NEW", 0);
}
if(groupedValue.get("IN_PROGRESS")==null){
groupedValue.put("IN_PROGRESS", 0);
}
}else{
groupedValue = new HashMap<>();
res.put(key, groupedValue);
groupedValue.put("CURRENCY", e.get("CURRENCY"));
groupedValue.put("PUBLISH_REGION", e.get("PUBLISH_REGION"));
groupedValue.put("SOURCE", e.get("SOURCE"));
groupedValue.put("RECON_STATUS", e.get("RECON_STATUS"));
groupedValue.put((String) e.get("STATUS"), e.get("JOB_ID_COUNT"));
}
return groupedValue;
}
static Map<String, Map<String, Object>> res = new HashMap<>();
public static void main(String[] args) {
List<Map<String, Object>> finalResult = new ArrayList<>();
lst.stream()
.map(MultipleFieldSorting2::mapper)
.forEach(result -> {
if(!finalResult.contains(result))
finalResult.add(result);
});
System.out.println(finalResult);
}
}
Tried this solution and it is working
Stream the source List
Map each value of map in the list to Class MapWrapper(a pojo where each key is a field)
GroupBy using the groupByKey defined in MapWrapper(uses CURRENCY, PUBLISH_REGION, SOURCE and RECON_STATUS columns)
3.a The result is a Map<String, List<MapWrapper>>
4.Stream through the entry set
map - and get the value alone from (Map<String, List<MapWrapper>>)
Map - convert from List<MapWrapper> to Map<String, Object> using MapWrapper::map
Collect to a list
In Short the solution is
List<Map<String, Object>> value = lst.stream()
.map(map -> new MapWrapper(map))
.collect(groupingBy(MapWrapper::groupByKey))
.entrySet()
.stream()
.map(e -> e.getValue())
.map(MapWrapper::map).collect(toList());
Working Code
public class MultipleFieldSorting {
private static Map<String, Object> map, map1, map2;
private static List<Map<String, Object>> lst = new ArrayList<>();
static {
map = new HashMap<>();
map.put("CURRENCY", "USD");
map.put("STATUS", "NEW");
map.put("PUBLISH_REGION", "DEL");
map.put("SOURCE", "ALADDIN");
map.put("RECON_STATUS", null);
map.put("JOB_ID_COUNT", "783");
map1 = new HashMap<>();
map1.put("CURRENCY", "USD");
map1.put("STATUS", "IN_PROGRESS");
map1.put("PUBLISH_REGION", "DEL");
map1.put("SOURCE", "ALADDIN");
map1.put("RECON_STATUS", null);
map1.put("JOB_ID_COUNT", "462");
map2 = new HashMap<>();
map2.put("CURRENCY", "USD");
map2.put("STATUS", "NEW");
map2.put("PUBLISH_REGION", "DEL");
map2.put("SOURCE", "GROUP");
map2.put("RECON_STATUS", null);
map2.put("JOB_ID_COUNT", "4");
lst.add(map);
lst.add(map1);
lst.add(map2);
}
public static void main(String[] args) {
List<Map<String, Object>> value = lst.stream()
.map(map -> new MapWrapper(map))
.collect(groupingBy(MapWrapper::groupByKey))
.entrySet()
.stream()
.map(e -> e.getValue())
.map(MapWrapper::map).collect(toList());
System.out.println(value);
}
}
class MapWrapper {
private String currency;
private String status;
private String publish;
private String source;
private String recon_status;
private String job_id;
public MapWrapper(Map<String, Object> map) {
this.currency = (String) map.get("CURRENCY");
this.status = (String) map.get("STATUS");
this.publish = (String) map.get("PUBLISH_REGION");
this.source = (String) map.get("SOURCE");
this.recon_status = (String) map.get("RECON_STATUS");
this.job_id = (String) map.get("JOB_ID_COUNT");
}
String groupByKey() {
return new StringBuilder().append(this.getCurrency()).append("-").append(this.publish).append("-")
.append(this.source).append("-").append(this.recon_status).toString();
}
public static Map<String, Object> map(List<MapWrapper> lst){
Map<String, Object> res = new HashMap<>();
res.put("CURRENCY",lst.get(0).getCurrency());
res.put("PUBLISH_REGION",lst.get(0).getPublish());
res.put("SOURCE",lst.get(0).getSource());
res.put("RECON_STATUS",lst.get(0).getRecon_status());
for(MapWrapper m : lst){
res.put(m.getStatus(), m.getJob_id());
}
if(res.get("NEW")==null){
res.put("NEW", 0);
}
if(res.get("IN_PROGRESS")==null){
res.put("IN_PROGRESS", 0);
}
return res;
}
String getCurrency() {
return currency;
}
void setCurrency(String currency) {
this.currency = currency;
}
String getStatus() {
return status;
}
void setStatus(String status) {
this.status = status;
}
String getPublish() {
return publish;
}
void setPublish(String publish) {
this.publish = publish;
}
String getSource() {
return source;
}
void setSource(String source) {
this.source = source;
}
String getJob_id() {
return job_id;
}
void setJob_id(String job_id) {
this.job_id = job_id;
}
String getRecon_status() {
return recon_status;
}
void setRecon_status(String recon_status) {
this.recon_status = recon_status;
}
}

Kafka Streams API: Session Window incompatible types

I have the following snippet:
groupedStream.windowedBy(SessionWindows.with(Duration.ofSeconds(config.joinWindowSeconds)).grace(Duration.ZERO));
KTable<byte[], byte[]> mergedTable =
groupedStream
.reduce((aggregateValue, newValue) -> {
try {
Map<String, String> recentMap = MAPPER.readValue(new String(newValue), HashMap.class);
Map<String, String> aggregateMap = MAPPER.readValue(new String(newValue), HashMap.class);
aggregateMap.forEach(recentMap::putIfAbsent);
newValue = MAPPER.writeValueAsString(recentMap).getBytes();
} catch (Exception e) {
LOG.warn("Couldn't aggregate key grouped stream\n", e);
}
return newValue;
}, Materialized.with(Serdes.ByteArray(), Serdes.ByteArray()))
.suppress(Suppressed.untilWindowCloses(unbounded()));
I am getting the following compilation exception:
Error:(164, 63) java: incompatible types: org.apache.kafka.streams.kstream.Suppressed<org.apache.kafka.streams.kstream.Windowed> cannot be converted to org.apache.kafka.streams.kstream.Suppressed<? super byte[]>
I know that if I inline the windowedBy like so:
KTable<Windowed<byte[]>, byte[]> mergedTable =
groupedStream
.windowedBy(SessionWindows.with(Duration.ofSeconds(config.joinWindowSeconds)).grace(Duration.ZERO))
.reduce((aggregateValue, newValue) -> {
try {
Map<String, String> recentMap = MAPPER.readValue(new String(newValue), HashMap.class);
Map<String, String> aggregateMap = MAPPER.readValue(new String(newValue), HashMap.class);
aggregateMap.forEach(recentMap::putIfAbsent);
newValue = MAPPER.writeValueAsString(recentMap).getBytes();
} catch (Exception e) {
LOG.warn("Couldn't aggregate key grouped stream\n", e);
}
return newValue;
}, Materialized.with(Serdes.ByteArray(), Serdes.ByteArray()))
.suppress(Suppressed.untilWindowCloses(unbounded()));
It works, but I am not sure how to separate and split those two...
there are two issues here.
The first issue is that KGroupedStream.windowedBy(SessionWindows) returns an instance of a SessionWindowedKStream<K, V> and in your first example
groupedStream.windowedBy(SessionWindows.with(Duration.ofSeconds(config.joinWindowSeconds)).grace(Duration.ZERO));
You are not capturing the returned SessionWindowedKStream in a variable.
The second issue is in your first code example you have
KTable<byte[], byte[]> mergedTable
when it should be
KTable<Windowed<byte[]>, byte[]> mergedTable
as it is in your second example.
If you change the code to
SessionWindowedKStream<byte[], byte[]> sessionWindowedKStream = groupedStream.windowedBy(SessionWindows.with(Duration.ofSeconds(config.joinWindowSeconds)).grace(Duration.ZERO));
KTable<Windowed<byte[]>, byte[]> mergedTable =
sessionWindowedKStream
.reduce((aggregateValue, newValue) -> {...
Then it should compile fine.
HTH
Bill

MulitvaluedMap<String, String> to MultivaluedMap<String, Object>

I have a MultivaluedMap<String, String> strMap which i want to convert to MultivaluedMap<String, Object> objMap.
I tried exploring a few routes in the post below but none of them seem to work.
Converting Map<String,String> to Map<String,Object>
I had the same issue today, after a lot of searching, I came up with the following solutions:
public static MultivaluedMap<String, Object> asObjectHeaders(MultivaluedMap<String, String> headers) {
if (headers == null) {
return null;
} else {
MultivaluedMap<String, Object> map = new MultivaluedHashMap<>();
for (Map.Entry<String, List<String>> entry : headers.entrySet()) {
if (entry.getValue() != null) {
map.addAll(entry.getKey(), new LinkedList<Object>(entry.getValue()));
}
}
return map;
}
}
OR If you are using Guava, then you can use the following code:
public static MultivaluedMap<String, Object> asObjectHeaders(MultivaluedMap<String, String> headers) {
if (headers == null) {
return null;
} else {
return new AbstractMultivaluedMap<String, Object>(Maps.transformValues(headers, new Function<List<String>, List<Object>>() {
#Override
public #Nullable List<Object> apply(#Nullable List<String> strings) {
if (strings != null) {
return new LinkedList<>(strings);
} else {
return null;
}
}
})) {};
}
}
OR If you are using jersey client glassfish, then you can use the following:
public static MultivaluedMap<String, Object> asObjectHeaders(MultivaluedMap<String, String> headers) {
if (headers == null) {
return null;
} else {
return new AbstractMultivaluedMap<String, Object>(Views.mapView(headers, LinkedList::new)) {};
}
}
Had the same situation, Working solution-
MultiValuedMap<String, String> multivaluedMap = //store the value here
Collection<Map.Entry<String, String>> entries = multivaluedMap.entries();
for(Map.Entry<String, String> ent : multivaluedMap.entries()){
entityList.add(ent.getKey()+ " " + ent.getValue());
}

how to define hashmap within hashmap using object of other hashmap

HashMap<String, HashMap<String, String>> hm = new HashMap<String, HashMap<String, String>>();
hm.put("Title1","Key1");
for(int i=0;i<2;i++) {
HashMap<String, String> hm1 = new HashMap<String, String>();
hm1.put("Key1","Value1");
}
if i have call Title1 that time they call another hashmap. i want
this type of output
hm<key,value(object hm1)>
hm<key,value)
first hashmap object call second hashmap key
If I correct undestand what you want, use following code
HashMap<String, HashMap<String, String>> hm = new HashMap<>();
HashMap<String, String> hm1 = new HashMap<>();
for(int i=0;i<2;i++) {
hm1.put("Key1","Value1");
}
hm.put("Title1", hm1); // save hm
...
HashMap<String, String> hm2 = hm.get("Title1");
String s = hm2.get("Key1"); // s = "Value1"
OR you can create new class
class HashKey {
private String title;
private String key;
...
// getters, setters, constructor, hashcode and equals
}
and just use HashMap < HashKey, String > hm, for example:
hm.put(new HashKey("Title1", "Key 1"), "Value");
...
String s = hm.get(new HashKey("Title1", "Key 1")); // Value
you can do something likewise,
HashMap<String,HashMap<String,String>> hm = new HashMap<String,HashMap<String,String>>();
HashMap<String,String> hm1 = new HashMap<String,String>();
hm1.put("subkey1","subvalue");
hm.put("Title1",hm1);
HashMap<String,String> newhm = hm.get("Title1");
import java.util.HashMap;
import java.util.Map;
public class MapInMap {
Map<String, Map<String, String>> standards =
new HashMap<String, Map<String, String>>();
void addValues() {
Map<String, String> studentA = new HashMap<String, String>();
studentA.put("A1", "49");
studentA.put("A2", "45");
studentA.put("A3", "43");
studentA.put("A4", "39");
standards.put("A", studentA);
Map<String, String> studentB = new HashMap<String, String>();
studentB.put("B1", "29");
studentB.put("B2", "25");
studentB.put("B3", "33");
studentB.put("B4", "29");
standards.put("B", studentB);
}
void disp() {
for (Map.Entry<String, Map<String, String>> entryL1 : standards
.entrySet()) {
System.out.println("Standard :" + entryL1.getKey());
for (Map.Entry<String, String> entryL2 : entryL1.getValue()
.entrySet()) {
System.out.println(entryL2.getKey() + "/" + entryL2.getValue());
}
}
}
public static void main(String args[]) {
MapInMap inMap = new MapInMap();
inMap.addValues();
inMap.disp();
}
}

Can't retrieve Double from HashMap<Integer, Double>

Somehow I can't retrieve a Double from a HashMap I've made using Gson.
Map<Integer, Double> ratingMap = (Map<Integer, Double>) new GsonBuilder()
.create().fromJson(json, Map.class);
Integer ifilmId = filmId;
Double rating = ratingMap.get(ifilmId);
In this code I've veried that the ratingMap contains {2=5.0}, but when I do ratingMap.get(ifilmId) (where I've verified that ifilmId is in fact 2), the variable rating is null. Am I missing something here?
I create the HashMap in the following way:
if (json.equals("")) {
// noting ever saved
ratingMap = new HashMap<Integer, Integer>();
ratingMap.put(filmId, rating);
} else {
ratingMap = (Map<Integer, Integer>) new GsonBuilder().create()
.fromJson(json, Map.class);
ratingMap.put(Integer.valueOf(filmId), rating);
}
I let Gson format the Integer to a Double, and that seems to work fine but I can't retrieve it.
The total code, including saving to Androids SharedPreferences
public void saveRating(int rating, int filmId) {
SharedPreferences sharedPref = context.getSharedPreferences(
LOCAL_MEM_KEY, 0);
String json = sharedPref.getString(LOCAL_MAP_RATING_KEY, "");
Map<Integer, Integer> ratingMap;
if (json.equals("")) {
// noting ever saved
ratingMap = new HashMap<Integer, Integer>();
ratingMap.put(filmId, rating);
} else {
ratingMap = (Map<Integer, Integer>) new GsonBuilder().create()
.fromJson(json, Map.class);
ratingMap.put(Integer.valueOf(filmId), rating);
}
json = new GsonBuilder().create().toJson(ratingMap, Map.class);
SharedPreferences.Editor editor = sharedPref.edit();
editor.putString(LOCAL_MAP_RATING_KEY, json);
editor.commit();
}
/*
* returns null if no rating found
*/
public Map<Integer, Integer> getRatingFor(int filmId) {
SharedPreferences sharedPref = context.getSharedPreferences(
LOCAL_MEM_KEY, 0);
String json = sharedPref.getString(LOCAL_MAP_RATING_KEY, "");
if (json.equals("")) {
return null;
}
Map<Integer, Integer> ratingMap = (Map<Integer, Integer>) new GsonBuilder()
.create().fromJson(json, Map.class);
Log.d("map", ratingMap.toString());
Integer ifilmId = filmId;
Integer rating = ratingMap.get(ifilmId);
if(rating == null) { //because of this we have to prevent a 0 rating
return null;
} else {
Map<Integer, Integer> returnMap = new HashMap<Integer, Integer>();
returnMap.put(filmId, rating.intValue());
return returnMap;
}
}
Make sure your not passing a null variable when saving
saveRating(int rating, int filmId){
Log.d(TAG, String.valueOf(rating));
}
if (json.equals("")) {
// noting ever saved
ratingMap = new HashMap<Integer, Double>(); <--- Double not Integer
ratingMap.put(filmId, 5.0);
} else {
ratingMap = (Map<Integer, Double>) new GsonBuilder().create()
.fromJson(json, Map.class); <--- double not Integer
ratingMap.put(Integer.valueOf(filmId), 5.0);
}
Make sure when using Doubles to
use 5.0
not 5
public void saveRating(Double rating, int filmId) {
SharedPreferences sharedPref = context.getSharedPreferences(LOCAL_MEM_KEY, 0);
String json = sharedPref.getString(LOCAL_MAP_RATING_KEY, "");
Map<Integer, Double> ratingMap;
if (json.equals("")) {
// noting ever saved
ratingMap = new HashMap<Integer, Double>();
} else {
ratingMap = (Map<Integer, Double>) new GsonBuilder().create().fromJson(json, Map.class);
}
ratingMap.put(filmId, rating);
ratingMap.put(3, 5.0d); // JUST FOR TEST
json = new GsonBuilder().create().toJson(ratingMap, Map.class);
SharedPreferences.Editor editor = sharedPref.edit();
editor.putString(LOCAL_MAP_RATING_KEY, json);
editor.commit();
}
/*
* returns null if no rating found
*/
public Map<Integer, Double> getRatingFor(int filmId) {
SharedPreferences sharedPref = context.getSharedPreferences(LOCAL_MEM_KEY, 0);
String json = sharedPref.getString(LOCAL_MAP_RATING_KEY, "");
if (json.equals("")) {
return null;
}
Map<Integer, Double> ratingMap = (Map<Integer, Double>) new GsonBuilder().create().fromJson(json, Map.class);
Log.d("map", ratingMap.toString());
Log.d("map", ratingMap.get(3) + ""); // JUST FOR TEST
Integer ifilmId = filmId;
Double rating = ratingMap.get(ifilmId);
if (rating == null) { //because of this we have to prevent a 0 rating
return null;
} else {
Map<Integer, Double> returnMap = new HashMap<Integer, Double>();
returnMap.put(filmId, rating);
return returnMap;
}
}

Categories

Resources