Generate object using Java streams - java

I have a question regarding Java streams. I built below maps.
One map includes country name as key and list of cities as value.
Another includes Continent name as key and list of countries as value.
List<String> inCities = new ArrayList<String>(Arrays.asList("Delhi", "Mumbai", "Hyderabad", "Banglore", "Chennai"));
List<String> jpCities = new ArrayList<String>(Arrays.asList("Tokyo", "Osaka", "Kyoto"));
List<String> usCities = new ArrayList<String>(Arrays.asList("Dallas", "Chicago", "NewYork"));
List<String> ukCities = new ArrayList<String>(Arrays.asList("London", "Cardiff", "Oxford"));
List<String> frCities = new ArrayList<String>(Arrays.asList("Paris", "Marseille", "Lyon"));
Map<String, List<String>> countryWiseCities = new HashMap<String, List<String>>();
countryWiseCities.put("India", inCities);
countryWiseCities.put("Japan", jpCities);
countryWiseCities.put("USA", usCities);
countryWiseCities.put("UK", ukCities);
countryWiseCities.put("France", frCities);
List<String> asiaCountries = new ArrayList<String>(Arrays.asList("India", "Japan"));
List<String> northAmericaCountries = new ArrayList<String>(Arrays.asList("USA", "Canada"));
List<String> europeCountries = new ArrayList<String>(Arrays.asList("UK", "France"));
Map<String, List<String>> continentWiseCountries = new HashMap<String, List<String>>();
continentWiseCountries.put("Asia", asiaCountries);
continentWiseCountries.put("NorthAmerica", northAmericaCountries);
continentWiseCountries.put("Europe", europeCountries);
Existing maps.
{"India":["Delhi", "Mumbai", "Hyderabad", "Banglore", "Chennai"], "Japan":["Tokyo", "Osaka", "Kyoto"], "USA":["Dallas", "Chicago", "NewYork"], "Canada":["Ontario", "Toronto", "vancouver"],
"UK":["London", "Cardiff", "Oxford"], "France":["Paris", "Marseille", "Lyon"]}
{"Asia":["India", "Japan"], "NorthAmerica":["USA", "Canada"], "Europe":["UK", "France"]}
I am looking for a way to build below map using java streams. What is the simplest way to achieve below output using Java streams?.
Expected output:
{"ASIA":["Delhi", "Mumbai", "Hyderabad", "Banglore", "Chennai", "Tokyo", "Osaka", "Kyoto"],
"NorthAmerica":["Dallas", "Chicago", "NewYork", "Ontario", "Toronto", "vancouver"],
"Europe":["London", "Cardiff", "Oxford", "Paris", "Marseille", "Lyon"]
Here key is continent name.
Value is list cities in countries in a particular continent.
Below is the code which I tried.
Map<String, List<Map.Entry<String, List<String>>>> collect1 = countryWiseCities.entrySet().stream()
.collect(Collectors.groupingBy(Map.Entry::getKey));

Here's one thing you can do:
Map<String, List<String>> continentCities = continentWiseCountries.entrySet()
.stream()
.map(continent -> new AbstractMap.SimpleEntry<>(
continent.getKey(),
continent.getValue()
.stream()
.flatMap(country ->
countryWiseCities.getOrDefault(country, Collections.emptyList())
.stream())
.collect(Collectors.toList())))
.collect(Collectors.toMap(Entry::getKey, Entry::getValue));
All it's doing is using an intermediate stream to join from continents to cities (through countries), then collecting cities and continent pairs.
Note that getOrDefault(country, Collections.emptyList()) is needed because you have countries in your continent list that do not exist in your country-list mapping.
The output of the above code is something like this:
{
"Asia" : [ "Delhi", "Mumbai", "Hyderabad", "Banglore", "Chennai", "Tokyo", "Osaka", "Kyoto" ],
"Europe" : [ "London", "Cardiff", "Oxford", "Paris", "Marseille", "Lyon" ],
"NorthAmerica" : [ "Dallas", "Chicago", "NewYork" ]
}

Here a solution without the intermediate stream, just using the flatMap in the value-function of the toMap-collector:
Map<String, List<String>> continentCityMap = continentWiseCountries.entrySet().stream()
.collect(Collectors.toMap(Entry::getKey,
e -> e.getValue().stream().flatMap(
country -> countryWiseCities.getOrDefault(country, Collections.emptyList()).stream())
.collect(Collectors.toList())));

Related

Fixture Generator For 8 Groups in Java

There are 4 different teams in 8 different groups that I created with Hashmap. I want 4 teams in each group to play a total of 12 matches, GetAway(away game) and GetHome(home game). I created a fixtur generator. I want to make separate rounds for each 8 groups. How do I create fixtures with the 8 groups I created?In the matches between the teams, the goals will be random.
Map<String, String> bagOne = Map.of("Bayern Munich", "Germany", "Sevilla", "Spaın", "Real Madrid", "Spaın", "Liverpool", "England", "Juventus", "Italy", "Paris Saint", "France", "Zenit", "Rusia", "Porto", "Rusia");
Map<String, String> bagTwo = Map.of("Barselona", "Spaın", "Atletico Madrid", "Spaın", "Manchester City", "England", "Manchester United", "England", "Borussia Dortmund", "Germany", "Shakhtar Donetsk", "Ukrayna", "Chelsea", "England", "Ajax", "Hollanda");
Map<String, String> bagThree = Map.of("Dynamo Kiev", "Ukrayna", "Redbull Salzburg", "Germany", "RB Leibzig", "Germany", "Internazionale", "Italy", "Olympiacos", "Italy", "Lazio", "Italy", "Krasnodar", "Rusia", "Atalanta", "Italy");
Map<String, String> bagFour = Map.of("Lokomotiv M", "Rusia", "Marseille", "France", "Club Brugge", "Belçika", "BorMönchengladbach", "Germany", "Galatasaray", "Türkiye", "Midtjylland", "Danimarka", "Rennes", "France", "Ferenevaros", "Macaristan");
List<Map<String, String>> remainingBags = new ArrayList<>();
remainingBags.add(bagTwo);
remainingBags.add(bagThree);
remainingBags.add(bagFour);
List<String> bagOneTeams = new ArrayList<String>(bagOne.keySet());
Collections.shuffle(bagOneTeams);
List<List<String>> remainingTeams = new ArrayList<>();
remainingTeams.add(new ArrayList<>(bagTwo.keySet()));
remainingTeams.add(new ArrayList<>(bagThree.keySet()));
remainingTeams.add(new ArrayList<>(bagFour.keySet()));
Collections.shuffle(remainingTeams.get(0));
Collections.shuffle(remainingTeams.get(1));
Collections.shuffle(remainingTeams.get(2));
List<LinkedHashMap<String, String>> resultingTeams = new ArrayList<>();
IntStream.range(0, 8).forEach(i-> {
LinkedHashMap<String, String> team = new LinkedHashMap<>();
team.put(bagOneTeams.get(i), bagOne.get(bagOneTeams.get(i)));
IntStream.range(0, 3).forEach(j -> {
String nextTeam = getNonDuplicateCountryTeam(remainingTeams.get(j), team.keySet(), remainingBags.get(j), team.values());
team.put(nextTeam, remainingBags.get(j).get(nextTeam));
});
resultingTeams.add(team);
});

Using streams to extract specific entries of a List of Maps in to a new Map

Given a org.bson.Document
{
"doneDate":"",
"todoEstimates":"",
"forecastDate":"",
"cardType":{
"projectData":[
{
"color":"#ffcd03",
"boardId":"30022"
},
{
"color":"#ffcd03",
"boardId":"1559427"
}
],
"cardFields":[
{
"fieldName":"id",
"fieldLabel":"Unique ID",
"fieldType":"Integer",
"itemType":"Long",
"isRequired":"NO",
"isReadOnly":"Yes",
"isDisabled":"NO",
"inputMethod":"System Generated",
"defaultValue":null,
"isUserType":"No"
},
{
"fieldName":"name",
"fieldLabel":"Title",
"fieldType":"Single-Line Text",
"itemType":"String",
"isRequired":"Yes",
"isReadOnly":"NO",
"isDisabled":"NO",
"inputMethod":"Manual Entry",
"defaultValue":null,
"isUserType":"No"
}
]
}
How do I extract the values of fieldName and fieldLabel via streams into the following?
{
"id": "Unique ID",
"name:" "Title",
...
}
I tried the following but I get stuck at the part where I get value of the cardFields list.
document.entrySet().stream().filter(e -> e.getKey().equals("cardType"))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue))
.entrySet().stream().filter(e -> e.getKey().equals("cardFields"))
.map(e -> (Map)e.getValue()).toList();
Here is a working solution with streams:
Map<String, Object> fields = ((List<Map<String, Object>>) ((Map<String, Object>) document.entrySet()
.stream()
.filter(entry -> entry.getKey().equals("cardType"))
.findFirst()
.orElseThrow(() -> new RuntimeException("card type not found"))
.getValue())
.entrySet()
.stream()
.filter(entry -> entry.getKey().equals("cardFields"))
.findFirst()
.orElseThrow(() -> new RuntimeException("card fields not found"))
.getValue())
.stream()
.collect(Collectors.toMap(el -> el.get("fieldName").toString(), element -> element.get("fieldLabel")));
Document result = new Document(fields);
System.out.println(result.toJson());
That's probably the worst code i have written - absolutely unreadable and you can't debug it. I would suggest that you do not use stream for this particular task, it isn't the right tool for it. So here is another working solution using Map.get(key):
Map<String, Object> cardType = (Map<String, Object>) document.get("cardType");
List<Map<String, Object>> cardFields = (List<Map<String, Object>>) cardType.get("cardFields");
Document result = new Document();
cardFields.forEach(cardField -> result.put((String) cardField.get("fieldName"), cardField.get("fieldLabel")));
System.out.println(result.toJson());
This is shorter, readable, you can debug it if needed and probably it's more performant. I'd say it's much better overall.
You may be able to parse your document like this:
Document cardType = document.get("cardType", Document.class);
final Class<? extends List> listOfMaps = new ArrayList<Map<String, String>>().getClass();
List<Map<String, String>> fields = cardType.get("cardFields", listOfMaps);
fields.stream().map(f -> {
System.out.println(f.get("fieldName") + ": " + f.get("fieldLabel"));
// here you can construct your new object
}).collect(Collectors.toList());
If you don't mind casting a lot, you could try following:
List cardFields = (List) ((Map) document.get("cardType")).get("cardFields");
Map<String, String> map = (Map) cardFields.stream()
.collect(Collectors.toMap(cf -> ((Document) cf).getString("fieldName"),
cv -> ((Document) cv).getString("fieldLabel")));
System.out.println(map);
or you can emit omit the casting with the following:
List<Document> carFields = document.get("cardType", Document.class)
.getList("cardFields", Document.class);
Map<String, String> map = carFields.stream()
.collect(Collectors.toMap(k -> k.getString("fieldName"),
v -> v.getString("fieldLabel")));
System.out.println(map);
Here is the complete example running with java 17:
import org.bson.Document;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class Bson {
private String json=
"""
{
"doneDate": "",
"todoEstimates": "",
"forecastDate": "",
"cardType": {
"projectData": [
{
"color": "#ffcd03",
"boardId": "30022"
},
{
"color": "#ffcd03",
"boardId": "1559427"
}
],
"cardFields": [
{
"fieldName": "id",
"fieldLabel": "Unique ID",
"fieldType": "Integer",
"itemType": "Long",
"isRequired": "NO",
"isReadOnly": "Yes",
"isDisabled": "NO",
"inputMethod": "System Generated",
"defaultValue": null,
"isUserType": "No"
},
{
"fieldName": "name",
"fieldLabel": "Title",
"fieldType": "Single-Line Text",
"itemType": "String",
"isRequired": "Yes",
"isReadOnly": "NO",
"isDisabled": "NO",
"inputMethod": "Manual Entry",
"defaultValue": null,
"isUserType": "No"
}
]
}
}
""";
public static void main(String[] args) {
Bson bson = new Bson();
Document document = Document.parse(bson.json);
List cardType = (List) ((Map) document.get("cardType")).get("cardFields");
Map<String, String> map = (Map) cardType.stream()
.collect(Collectors.toMap(cf -> ((Document) cf).getString("fieldName"),
cv -> ((Document) cv).getString("fieldLabel")));
System.out.println(map);
List<Document> carFields = document.get("cardType", Document.class).getList("cardFields", Document.class);
Map<String, String> map1 = carFields.stream()
.collect(Collectors.toMap(k -> k.getString("fieldName"), v -> v.getString("fieldLabel")));
System.out.println(map1);
}
}

How to convert a list of java objects to Map<String, Map<String, String>>

I have a list of java objects as below:
[
{
id: "frwfhfijvfhviufhbviufg",
country_code: "DE",
message_key: "key1",
translation: "This is the deutsch translation"
},
{
id: "dfregregtegetgetgttegt",
country_code: "GB",
message_key: "key1",
translation: "This is the uk translation"
},
{
id: "frffgfbgbgbgbgbgbgbgbg",
country_code: "DE",
message_key: "key2",
translation: "This is the again deutch translation"
}
]
How can I convert this into a Map<String, Map<String, String>> like below:
{
"DE": {
"key1": "This is the deutsch translation",
"key2": "This is the again deutch translation"
},
"GB": {
"key1": "This is the uk translation"
}
}
I am new to java and below is my code but the code is not correct:
Map<String, Translations> distinctTranslations = customTranslationsEntities
.stream().collect(Collectors.groupingBy(
CustomTranslationsEntity::getCountryCode,
Collectors.toMap(
CustomTranslationsEntity::getMessageKey,
CustomTranslationsEntity::getTranslation),
)))
where Translations is proto buffer message like below:
message Translations {
map<string, string> translations = 1;
}
Here map<string, string> translations means map like "key1", "This is the deutsch translation"...like this.
The output should be Map<String, Map<String,String>>:
Map<String, Map<String,String>>
distinctTranslations = customTranslationsEntities
.stream()
.collect(Collectors.groupingBy(CustomTranslationsEntity::getCountryCode,
Collectors.toMap(
CustomTranslationsEntity::getMessageKey,
CustomTranslationsEntity::getTranslation,
(v1,v2)->v1)));
I added a merge function, in case there are duplicate keys.
If you want to do it without using streams then
private List<MyObject> list = // Your object List
private Map<String, Map<String, String>> map = new HashMap<>();
for(MyObject object : list){
Map<String, String> localMap;
localMap = map.getOrDefault(object.country_code, new HashMap<>());
localMap.put(object.message_key, object.translation);
if(!map.containsKey(object.country_code)){
map.put(object.country_code, localMap);
}
}

java 8 List<Object[]> to Map<String, Map<String, BigDecimal>>

The list contains Arrays of (String,String,BigDecimal).
I want to convert the List to Map<String, Map<String, BigDecimal>>
grouped by the first String using java 8 Stream class.
List<Object[]> list = ss.createCriteria(PayrollDeduction.class)
.createAlias("payroll", "pay")
.add(Restrictions.le("pay.paymentDate", atDate))
.createAlias("payroll.employee", "employee")
.add(Restrictions.in("employee.id", Arrays.asList(empId)))
.setProjection(Projections.projectionList()
.add(Projections.property("gouvAgencyCode"))
.add(Projections.property("code"))
.add(Projections.sum("amount"))
.add(Projections.groupProperty("gouvAgencyCode"))
.add(Projections.groupProperty("code")))
.setReadOnly(true)
.list();
list.stream().collect(Collectors.groupingBy(r->r[0], Collectors.mapping(mapper, downstream)));
Do it like this:
List<Object[]> list = Arrays.asList(
new Object[] { "A", "X", new BigDecimal("1") },
new Object[] { "A", "Y", new BigDecimal("2.0") },
new Object[] { "B", "X", new BigDecimal("3.00") },
new Object[] { "C", "Z", new BigDecimal("4.000") }
);
Map<String, Map<String, BigDecimal>> map = list.stream()
.collect(Collectors.groupingBy(r -> (String) r[0],
Collectors.toMap(r -> (String) r[1],
r -> (BigDecimal) r[2])));
System.out.println(map);
Output
{A={X=1, Y=2.0}, B={X=3.00}, C={Z=4.000}}
To me, it looks like a XY Problem:
You seem to be using Hibernate;
You want to transform the results from your projection into some other format.
Hibernate's own ResultTransformer could have solved your problem in a cleaner way.

Is there a direct way to convert Map<String, List<String>> into a JSON String (using javax.json library)

Lets assume I have the following Map
Map<String, List<String>> map = new HashMap<>();
List<String> list = new ArrayList<>();
list.add("First in list");
list.add("Second in list");
map.put("First in map", list);
So how can I convert it in a direct way to a JSON String using javax.json library?
I just tried the following
Map<String, List<String>> map = new HashMap<>();
List<String> list = new ArrayList<>();
list.add("First in list");
list.add("Second in list");
List<String> list1 = new ArrayList<>();
list1.add("First in secondlist");
list1.add("Second in secondlist");
map.put("First in map", list);
map.put("Second in map", list1);
final JsonArrayBuilder outerJsonArray = Json.createArrayBuilder();
map.forEach((key, innerList) -> {
JsonArrayBuilder innerJsonArray = Json.createArrayBuilder();
innerList.forEach(item -> innerJsonArray.add(item));
outerJsonArray.add(Json.createObjectBuilder().add(key, innerJsonArray));
});
JsonArray usersJson = outerJsonArray.build();
System.out.println(usersJson.toString());
And the resulting JSON array is (missing the insertion order)
[
{
"Second in map": [
"First in secondlist",
"Second in secondlist"
]
},
{
"First in map": [
"First in list",
"Second in list"
]
}
]

Categories