How to convert a Map to Arraylist of Objects? - java

Suppose I have having Json response like this:
{
"status": true,
"data": {
"29": "Hardik sheth",
"30": "Kavit Gosvami"
}
}
I am using Retrofit to parse Json response. As per this answer I will have to use Map<String, String> which will give all the data in Map. Now what I want is ArrayList<PojoObject>.
PojoObject.class
public class PojoObject {
private String mapKey, mapValue;
public String getMapKey() {
return mapKey;
}
public void setMapKey(String mapKey) {
this.mapKey = mapKey;
}
public String getMapValue() {
return mapValue;
}
public void setMapValue(String mapValue) {
this.mapValue = mapValue;
}
}
What is the best way to convert a Map<key,value> to a List<PojoObject>?

If you can expand your class to have a constructor taking the values as well:
map.entrySet()
.stream()
.map(e -> new PojoObject(e.getKey(), e.getValue()))
.collect(Collectors.toList());
If you can't:
map.entrySet()
.stream()
.map(e -> {
PojoObject po = new PojoObject();
po.setMapKey(e.getKey());
po.setMapValue(e.getValue());
return po;
}).collect(Collectors.toList());
Note that this uses Java 8 Stream API.

Looks like Java has exact POJO Map.Entry like you want. Hence, you can extract the entry set from map and iterate over the entry set like below or you can further convert the set to list like in next snippet and continue with your processing.
//fetch entry set from map
Set<Entry<String, String>> set = map.entrySet();
for(Entry<String, String> entry: set) {
System.out.println(entry.getKey() +"," + entry.getValue());
}
//convert set to list
List<Entry<String, String>> list = new ArrayList(set);
for(Entry<String, String> entry: list) {
System.out.println(entry.getKey() +"," + entry.getValue());
}

Try this
List<Value> list = new ArrayList<Value>(map.values());
Or
hashMap.keySet().toArray(); // returns an array of keys
hashMap.values().toArray(); // returns an array of values
Should be noted that the ordering of both arrays may not be the same.
or
hashMap.entrySet().toArray();

You can use this method to convert map to list
List<PojoObject> list = new ArrayList<PojoObject>(map.values());
Assuming:
Map <Key,Value> map;

ArrayList<Map<String,String>> list = new ArrayList<Map<String,String>>();
this may be the best way.

Related

Java8 Stream grouping by with toMap issue

I have a class named ConfigKey
public class ConfigKey {
String code;
String key;
String value;
//omit setter and getter
}
I want to convert List<ConfigKey> to Map<String, Map<String, Object>>, here is my method definition
public Map<String, Map<String, Object> convert (List<ConfigKey> list) {
return list.stream().collect(Collectors.groupingBy(ConfigKey::getCode,
Collectors.toMap(ConfigKey::getKey, ConfigKey::getValue)));
}
however I want to do some changes, for each ConfigKey put another key to the map, e.g.
{ "code": "code1","key", "key1", "value": "value1"}
to Map
{"code1": {"key1":"value1", "prefix_key1": "value1" }
is there any API to do it like bellow:
public Map<String, Map<String, Object> convert (List<ConfigKey> list) {
return list.stream().collect(Collectors.groupingBy(ConfigKey::getCode,
Collectors.toMap("prefix_" + ConfigKey::getKey, ConfigKey::getValue))
Collectors.toMap(ConfigKey::getKey, ConfigKey::getValue)));
}
You can make use of the Collector.of() factory method, which allows you to create your own collector:
public Map<String, Map<String, Object> convert (List<ConfigKey> list) {
return list.stream().collect(Collectors.groupingBy(ConfigKey::getCode, Collector.of(
HashMap::new, (m, c) -> {
m.put(c.getKey(), c.getValue());
m.put("prefix_" + c.getKey(), c.getValue());
}, (a, b) -> {
a.putAll(b);
return b;
}
)));
}
But honestly that seems a bit messy, and maybe a normal loop would've been better. The streams intention was to provide an api which does things in a more readable manner, but when you have to hackaround that construct, by introducing some extremely unreadable logic then it is almost always the better option to just do it the old way:
public Map<String, Map<String, Object> convert (List<ConfigKey> list) {
Map<String, Map<String, Object>> map = new HashMap<>();
for (ConfigKey ck : list) {
Map<String, Object> inner = map.computeIfAbsent(ck.getCode(), k -> new HashMap<>());
inner.put(ck.getKey(), ck.getValue());
inner.put("prefix_" + ck.getKey(), ck.getValue());
}
return map;
}
You can first add the new entries to the map and then group them:
private Map<String, Map<String, Object>> convert(List<ConfigKey> list) {
new ArrayList<>(list).stream().map(configKey -> new ConfigKey(configKey.getCode(), "prefix_" + configKey.getKey(), configKey.getValue())).forEachOrdered(list::add);
return list.stream().collect(Collectors.groupingBy(ConfigKey::getCode,
Collectors.toMap(ConfigKey::getKey, ConfigKey::getValue)));
}
I cloned the list (in order to prevent ConcurrentModificationException), then changed the keys to the "new" ones (with map) and added them to the original list - forEachOrdered(list::add).
Because the 'code' field was not changed, both entries will use it which results in 2 entries in the map

get key and value from HashMap within ArrayList

I have a file that i get all the data and separate it into a HashMap.
The file looks something like this below.
Before the : is the key and after is the value
key1: 1
key2: 2
key3: 3
this is the code that puts the file data into the map ArrayList:
protected List<Map<String, String>> yaml_parse(BufferedReader filename) throws IOException{
String result;
List<Map<String, String>> list = new ArrayList<Map<String, String>>();
while ((result = filename.readLine()) != null) {
Map<String, String> map = new HashMap<String, String>();
String key = result.substring(0, result.indexOf(":"));
String value = result.substring(result.lastIndexOf(":") + 2);
map.put(key, value);
list.add(map);
}
return list;
}
in another class where i call the function and println, this is the output
[{key1=1}, {key2=2}, {key3=3}]
So my Main question is, how do i get key1 and have it return its value?
I don't understand why you are creating a List of maps. A Map will let you put several key value pairs. Here is a way that would work:
protected Map<String, String> yaml_parse(BufferedReader filename) throws IOException{
String result;
Map<String, String> map = new HashMap<String, String>();
while ((result = filename.readLine()) != null) {
//keyValue[0] = key, keyValue[1] = value
String[] keyValue = result.split(": ");
map.put(keyValue[0], keyValue[1]);
}
return map;
}
And you would use it like this:
Map<String, String> map = yaml_parse("myFile.yaml");
String key1Value = map.get("key1"); //Stores key1's value into key1Value
I think you might be using the wrong data structure. From your question, it seems like you want a Map only, not a List of Maps.
You should look at changing your List<Map> to a Map. You can do this using:
Map<String, String> map = list.stream()
.flatMap(m -> m.entrySet().stream())
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
If you want to work with your current data structure, you can get the required value like this:
private Optional<String> getValue(List<Map<String, String>> list, String key) {
return list.stream()
.filter(m -> m.containsKey(key))
.map(m -> m.get(key))
.findFirst();
}
and use it as follows:-
Optional<String> value = getValue(list, "key2");
System.out.println(value.orElse(null));
So if you are interested in using java-8, if list of map contains any of entry with key as key1 will return the first entry value else it will return the default value
list.stream().flatMap(map->map.entrySet().stream().filter(entry->entry.getKey().equals("key1"))).findFirst()
.orElse(new AbstractMap.SimpleEntry("key1", "default value")).getValue();
Just by using normal for loop
for(Map<String, String> map : list) {
if(map.containsKey("key1")) {
result = map.get("key1");
break;
}
}
Are you sure this is the data structure you want?
A map can contain more than 1 key/value pair. Why not have a single hashmap here, containing all 3 key/value pairs, at which point, you can just do:
map.get("key1")
and it'll still be fast even if you have millions of these.
If you are making single-size maps and putting them into an arraylist because you want to preserve order, use LinkedHashMap. If you need to be capable of dealing with repeated keys, use guava's Multimap, or make a Map<String, List<String>>.

How to get specific values from an ArrayList of HashMaps

I have created an ArrayList of HashMaps and I know how to get all keys and values of all HashMaps in the list, but then I decided to make it complicated and iterate through the ArrayList and get only specific HashMap values(based on keys). I have no idea how to do that.
How can I modify printArrayList method to get only idand sku values from all hashmaps?
Right now I have the following example:
public class HashmapArraylist {
public static void main(String[] args) throws Exception {
Map<String, Object> map1 = new HashMap<>();
map1.put("id", 1);
map1.put("sku", "test1");
map1.put("quantity", 1);
Map<String, Object> map2 = new HashMap<>();
map2.put("id", 2);
map2.put("sku", "test2");
map2.put("quantity", 2);
Map<String, Object> map3 = new HashMap<>();
map3.put("id", 3);
map3.put("sku", "test3");
map3.put("quantity", 3);
ArrayList<Map<String, Object>> arrayList = new ArrayList<>();
arrayList.add(map1);
arrayList.add(map2);
arrayList.add(map3);
printArrayList(arrayList);
}
public static void printArrayList(ArrayList<Map<String, Object>> arrayList) {
for (Map<String, Object> entry : arrayList) {
for (String key : entry.keySet()) {
String value = entry.get(key).toString();
System.out.println(key + " : " + value);
}
System.out.println("-----------");
}
}
}
Your iterator for the arrayList is correct. To retrieve a value from a map, simply provide the key into the 'get' function of the entry. Since your map has a "String" key to an "Object" value, you can use "toString()" on it to get the string from the Object returned from your key.
public static void printArrayList(ArrayList<Map<String, Object>> arrayList) {
for (Map<String, Object> entry : arrayList) {
String myID = entry.get("id").toString();
String mySKU = entry.get("sku").toString();
System.out.print("id:" + myID + " sku: " + mySKU);
System.out.println("-------------------");
}
}
user681574 seems to have already answered your problem, but I will just add one Java8 example code to do the same thing as you need, using streams
public static void printArrayList(ArrayList<Map<String, Object>> arrayList) {
arrayList.stream() //stream out of arraylist
.forEach(map -> map.entrySet().stream() //iterate through each map in the list, create stream out of maps' entryset
.filter(entry -> entry.getKey().equals("id") || entry.getKey().equals("sku")) //filter out only entries that we need (where key is "id" or "sku")
.forEach(idOrSku -> System.out.println(idOrSku.getKey() + ":" + idOrSku.getValue()))); //Iterate through the id/sku entries and print them out just as we want to
}

how to return key-value list from a hash map

The input is a hash map, such like
HashMap<String, String> hashmap = new HashMap<String, String>();
for (Map.Entry<String, String> entry : hashmap.entrySet()) {
String key = entry.getKey();
Object value = entry.getValue();
}
I would like to write a method that return list of type Class A, which had key, value attributes with String type, and the key-value from hashmap.
How to make it real?
If you are using Java 8, you could do something like this:
List<Entry<String, String>> list = hashmap
.entrySet() // Get the set of (key,value)
.stream() // Transform to a stream
.collect(Collectors.toList()); // Convert to a list.
If you need a list of elements of type A, you can adapt:
List<A> list = hashmap
.entrySet() // Get the set of (key,value)
.stream() // Transform to a stream
.map(A::new) // Create objects of type A
.collect(Collectors.toList()); // Convert to a list.
assuming that you have a constructor in A that looks like that:
A(Map.Entry<String,String> e){
this.key=e.getKey();
this.value=e.getValue();
}
I hope it helps.
List<A> listOfA= new ArrayList<>();
for (Map.Entry<String, String> entry : hashmap.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
A aClass = new A(key, value);
listOfA.add(aClass);
}
return listOfA;

Delete from map while adding to an array? (lambda)

I want to iterate through a Map, running a boolean function on the values and appending the true values to a List. I also want to remove the keys with false values from the map as I do this. How would I accomplish this with a lambda? From this answer I can do the latter but I am not sure how I would combine them in a lambda function.
Edit:
The map is
<String, String>
and I have a boolean method checkCondition(string)
If you want to remove the values equal to "2".
map.entrySet().removeIf(e -> e.getValue().equals("2") ? list.add(e.getValue()) : false);
Using a stream you would get a ConcurrentModificationException, but you can do it by combining an Iterator with a lambda expression.
Example:
List<String> myList = new ArrayList<>();
Map<String, String> myMap = new HashMap<>();
Predicate<String> filter = "foo"::equals;
myMap.put("a", "foo");
myMap.put("b", "bar");
Iterator<Map.Entry<String, String>> iterator = myMap.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, String> element = iterator.next();
if (filter.test(element.getValue())) {
myList.add(element.getValue());
} else {
iterator.remove();
}
}
System.out.println("List: " + myList);
System.out.println("Map: " + myMap);
The output will be:
List: [foo]
Map: {a=foo}
"bar" has been removed and "foo" has been added to the list.
This is one way to do it (although not the only one):
List<String> list = new ArrayList<>();
map = map.entrySet().stream()
.filter(t -> checkCondition(t.getValue()))
.peek(t -> list.add(t.getKey()))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

Categories