I have a map:
Map<Map<String,Map>, List<Map<String, String>>> map = new HashMap();
What is the easiest way to refactor the following data structure and create a class structure using OOP principles?
You should have a simple yet clear picture of what you want to achieve before starting your refactoring:
Can you break-down the data in your collection into business entities (orders, products, customers, ...) ?
What are the relationships between those data ? It could be a list of user linked to orders containing products.
Use the right Collection for the right usage. HashMap are key/value pair unordered collections. You access an element by its key (one key point to one value, no duplicate keys).
ArrayList are values only ordered collections. You access an element by its integer index.
Given the following collection:
HashMap<String, HashMap<String, String>> map = new HashMap<String, HashMap<String, String>>();
And its representation:
{
"Order-Ref-01": {
"id": "123456"
"client": "MrFooBar"
"destination": "USA"
"type": "Electronic"
},
"Order-Ref-02": {
//...
}
}
You could refactor it into a list of Orders because you decided fetching by key is not needed for example.
List<Order> orders = new ArrayList<>();
And you could create 2+ classes to serve as business entities:
class Client {
private String id;
private String name;
// ...
}
class Order {
private String id;
private Client client;
private String destination;
private String type;
// ...
}
It's all about breaking-down your data structure into smaller parts representing your business entities and their relationships.
Related
My json file looks like this [actually it has more, I am just putting 2 blocks for example]
[{
"answerValue": "2021-02-01",
"parentId": "Policy",
"instance": 1,
"fieldId": "PolicyEffectiveDate"
},
{
"answerValue": "2012",
"parentId": "Insured",
"instance": 1,
"fieldId": "DateBusinessStarted"
}
]
I want to store them in a HashMap and print them.
public void MapCheck() {
Map<String, Object> dataMap = new HashMap<>();
List<Map> lstMap = new ArrayList<>();
dataMap.put("answerValue:", "2021-02-01");
dataMap.put("parentId:", "Policy");
dataMap.put("instance:", 1);
dataMap.put("fieldId:", "PolicyEffectiveDate");
lstMap.add(dataMap);
dataMap.put("answerValue:", "Assurestart LLC");
dataMap.put("parentId:", "Insured");
dataMap.put("instance:", 1);
dataMap.put("fieldId:", "Business_Name");
lstMap.add(dataMap);
System.out.println(lstMap);
}
public static void main(String[] args) {
Test t = new Test();
t.MapCheck();
}
}
Expected: I wanted it to print
[{parentId:=Policy, fieldId:=PolicyEffectiveDate, answerValue:=2021-02-01, instance:=1}, {parentId:=Insured, fieldId:=Business_Name, answerValue:=Assurestart LLC, instance:=1}]
Actual: It is printing, the last value twice.
[{parentId:=Insured, fieldId:=Business_Name, answerValue:=Assurestart LLC, instance:=1}, {parentId:=Insured, fieldId:=Business_Name, answerValue:=Assurestart LLC, instance:=1}]
How can I make it print 2 different values? Thanks in advance for your time and ideas.
You should create a new map for the second entry instead of overwriting the first entry’s values. Add
dataMap = new HashMap<>();
After adding the first entry to the list.
You should create a new map for the second map in the list:
Map<String, Object> dataMap = new HashMap<>();
List<Map<String, Object>> lstMap = new ArrayList<>();
dataMap.put("answerValue:", "2021-02-01");
dataMap.put("parentId:", "Policy");
dataMap.put("instance:", 1);
dataMap.put("fieldId:", "PolicyEffectiveDate");
lstMap.add(dataMap);
dataMap = new HashMap<>(); // create a new map!
dataMap.put("answerValue:", "Assurestart LLC");
dataMap.put("parentId:", "Insured");
dataMap.put("instance:", 1);
dataMap.put("fieldId:", "Business_Name");
lstMap.add(dataMap);
That said, if you actually want to generate JSON, or read a JSON file, I recommend using a JSON serialisation/deserialisation library, such as GSON. That way, you can represent your data not as hash maps, but a class like this:
class MyObject {
private String answerValue;
private String parentId;
private int instance;
private String fieldId;
// getters & setters...
}
HashMap as you know is a data structure that works based on unique key and value pair property.
In the example above when you perform dataMap.put("answerValue:", "2021-02-01"); it saves the value for this key in the HashMap. However when you perform, dataMap.put("answerValue:", "Assurestart LLC"); the second time, it will override the value of "answerValue:" key as it already exists there.
A better approach is to create a new class that can contain all this data in it and then you can decide on a unique key to store this data in. Thus your values will be an object that contains this entire block of data.
For example,
public class MyData {
private String answerValue;
private String parentId;
private Integer instance;
private String fieldId;
//Setters and getters
...
}
We know that since Java 7, Switch's expression can be a String. So I was making an app, where, when a user selects a category he/she will be assigned the concerned department as per the category value. Here's the code:-
public class Selector {
///String array to save the departments
private final static String[] DEPTS = {
"A",
"B",
"C",
"D"
};
//String array for the categories
private final static String[] CATEGORY = {
"Wind",
"Air",
"Fire",
"Cloud",
"River",
"Tree",
"Abc",
"Def"
};
//return the department when user selects a particular category item from above
public static String setDepartment(String category) {
switch(category){
case "Wind":
return DEPTS[0];
case "Air":
return DEPTS[1];
case "Fire": case "Cloud": case "River":
return DEPTS[2];
case "Tree": case "Abc": case "Def":
return DEPTS[3];
}
return null;
}
}
So I was thinking just how I can return the department item using the array index of department, can I use the same thing in the case value, like,
case CATEGORY[0]: case CATEGORY[1]:
return DEPTS[2];
Cause if the category items contain a large string than the cases will become too long to write. If java doesn't allow this, can you suggest some other way so that my code doesn't become cumbersome? Thanks.
Why don't you use a enum to do that.
public class Selector {
private enum DepartmentCategory = {
Wind("A"),
Air("B"),
Fire("C"),
Cloud("C"),
River("C"),
Tree("D"),
Abc("D"),
Def("E");
private String department;
DepartmentCategory(String department) {
this.department = department;
}
public String getDepartment() {
return department;
}
};
}
Now if you are given a department, you can easily get the category by the following code.
String category = "Wind";
DepartmentCategory dc = DepartmentCategory.valueOf(category);
dc.getDepartment(); // Returns the department
You could use a Map<String, String> to map the category to the department.
Then instead of a switch, you'll have to use map.get(category) which will return the department.
You could make it a Map<String, List<String>> to map the department to the categories, and do
for(String dept : map.keySet())
{
if(map.get(dept).contains(category))
{
return dept;
}
}
Edit: With enums, that works well if there will be no extra departments or categories in the future. A map allows a more dynamic approach. Both work well, if used correctly
I would use a Map<String, String> to store your associations between department and category rather than a switch statements. It seems like you are trying to use an Array[] for a purpose in which it wasn't intended.
If you use a Map<String, String> then you can store your data as follows:
Map<String, String> departments = new HashMap<String, String>();
departments.add("Wind", "A")
departments.add("Fire", "B")
departments.add("Fire", "C")
departments.add("River", "C")
departments.add("Cloud", "C")
You can then easily retrieve departments names by using the syntax:
String category = "Cloud"
String department = departments.get(category)
If you want to associate multiple departments you can use Map<String, List<String>> to represent a relationship between a type and category and multiple departments.
Traditionally if you think about representing this in a database you would be using a map with an index and an object underneath.
I don't think you want to use Arrays to do this in your case.
I have come across this question on StackOverflow which asks about converting JSON to Java. The answer shows that another class is modelled to represent the JSON data as well as an object being created and I don't understand why.
Does that object now contain all the information after Gson reads the content or only one key/value pair? If it only contains 1 key/value pair, I'm assuming I would need to create multiple objects for the JSON that I have below which I can the use a loop to iterate over and add the values to a drop down menu?
{
"1": "Annie",
"2": "Olaf",
"3": "Galio",
"4": "TwistedFate",
"5": "XinZhao",
"6": "Urgot",
"7": "Leblanc",
"8": "Vladimir",
"9": "FiddleSticks",
"10": "Kayle",
"11": "MasterYi",
"12": "Alistar",
"13": "Ryze",
"14": "Sion",
"15": "Sivir",
"16": "Soraka",
"17": "Teemo",
"18": "Tristana",
"19": "Warwick",
"20": "Nunu"
}
Essentially what I am aiming to do is:
1) Create a list of names with the Values.
2) Sort the list of names (as it comes unsorted) in alphabetical order
3) Loop through the list and add each name to a drop down menu
4) When a name in the drop down menu is selected, the key associated with that value is passed to another url which receives more data.
Sorry if this is unclear. I've spent a couple of hours trying to understand how to get elements from JSON and display it, as well as trying to create a list where I can use the key to display information the name but have had no luck except for using a for-each loop.
Let's use Jackson's feature that allows you to map any property to a single method (you don't really need a getter here I believe). Just swap the key and value in this universal setter, and add to a TreeMap, which is already sorted by key (name). Then you can output the keys (names) in the alphabetical order and get an ID by name easily.
public static void main(String[] args) throws IOException {
String json = "....."; // your JSON string here
com.fasterxml.jackson.databind.ObjectMapper mapper =
new com.fasterxml.jackson.databind.ObjectMapper();
ReverseMap pairs = mapper.readValue(json, ReverseMap.class);
for (Map.Entry<Object, String> entry : pairs.getValues().entrySet()) {
System.out.println(entry.getKey() + ":" + entry.getValue());
}
}
public class ReverseMap {
private TreeMap<Object, String> mapping = new TreeMap<>();
#com.fasterxml.jackson.annotation.JsonAnySetter
public void add(String name, Object value) {
mapping.put(value, name);
}
public Map<Object, String> getValues() {
return mapping;
}
}
Gson Bean Mapping Solution
Okay, what you have is a bit unusual for a JSON object; the keys (the numbers in your case) essentially represent properties of their contained object. That's workable, but you have to understand that, for example, when looking for "Annie" in the JSON object, if you use Gson to map to a "bean" class, which we'll call Data (as in the linked example), then you'd have to create a data object like so:
class Data {
private String _1;
// ...
private String _20;
public String get1() { return _1; }
public void set1(String _1) { this._1 = _1; }
// ...
public String get20() { return _20; }
public void set20(String _20) { this._20 = _20; }
}
And by using Data data = new Gson().fromJson(myJsonString, Data.class); on the given string, you'd be able to find "Annie" by calling... uh... data.get1()?
Clearly, this isn't a good solution.
Better Solutions
Since your data doesn't follow the typical format for a JSON object, you have two options:
If you can, refactor your JSON representation to a more verbose, but better representation for parsing.
Use a different approach to parse the existing JSON.
Solution 1: Changing the JSON representation
Refactoring the JSON would result in an object that (preferably) would look like this:
{
"champions" : [
{
"index" : 1,
"name" : "Annie"
},
{
"index" : 2,
"name" : "Olaf"
},
// ...
]
}
This could map easily to a couple of beans that look like this:
class Data {
private List<Champion> champions;
// TODO getters and setters
}
class Champion {
private int index;
private String name;
// TODO getters and setters
}
However, this adds a lot of unnecessary clutter to the JSON object, and isn't really necessary with only two fields per champion (the name, and their index).
You could simplify that further like so:
{
"champions" : [
"Annie",
"Olaf",
// ...
]
}
The bean class for that would then be:
class Data {
private List<String> champions;
// TODO getters and setters
}
Much simpler, but still requires a change to the JSON you're getting, which in some situations isn't possible. If you used this, though, you could also get rid of the "bean" class entirely, via:
List<String> champions = (List<String>) new Gson().fromJson(myJsonString, new TypeToken<List<String>>(){}.getType());
Solution 2: Changing how the JSON is parsed
The arguably better and cleaner solution is just to change how the JSON is parsed.
The goal here (if I understand you correctly) is to parse the JSON and spit out a collection of strings representing each champion's name, accessible by the numeric index of the champion in the JSON representation.
As such, and because of the way the JSON object is laid out as a simple mapping of strings to strings, we can use Gson to pipe directly into a Map<String, Object>, like so:
Map<String, String> mappedValues = new Gson().fromJson(myJsonString, Map.class);
String anniesName = mappedValues.get("1"); // "Annie"
String olafsName = mappedValues.get("2"); // "Olaf"
boolean hasTwentyOneElements = mappedValues.containsKey("21"); // false
This is shorter, requires no "bean" classes, and keeps the original JSON representation. The downside is that you can't easily tell whether the indices of each entry are correct and consistent; ie. if someone types in the wrong number, or deletes one of the entries.
To get a container of all keys, you just use mappedValues.keySet(), and to get a container of all key-value pairs, you use mappedValues.entrySet(), which gives you a Set<Map.Entry<String, String>>. Both of those can be iterated over, and may be in random order (I'm not sure whether the underlying Map implementation preserves insertion order or not).
To get the index for a given name (ie. champ), you'd use something similar to the following:
String index = null;
for (Map.Entry<String, String> entry : mappedValues.entrySet()) {
if (champ.equals(entry.getValue())) {
index = entry.getKey();
break;
}
}
Of course, you'd have to check to see if index is null after this, and handle that appropriately, but it's easily doable.
EDIT: #vempo's answer provides a cleaner, more efficient lookup strategy by means of inverting the map (although the answer is written for Jackson, instead of Gson); an adaptation of this for Gson is as follows (and yes, there is a vastly superior version in java-8, left out for sake of availability):
public Map<String, String> invertMap(Map<String, String> input) {
Map<String, String> newMap = new LinkedTreeMap<String, String>(); // TODO Pick optimal storage class
for (Map.Entry<String, String> entry : input.entrySet()) {
newMap.put(entry.getValue(), entry.getKey());
}
return newMap;
}
// ...
Map<String, String> mappedValues = invertMap(new Gson().fromJson(myJsonString, Map.class));
String annieIndex = mappedValues.get("Annie"); // "1"
String olafIndex = mappedValues.get("Olaf"); // "2"
It's worth noting that this sacrifices efficiency of constructing the map by effectively building it twice (once by Gson and once more to invert), but it makes value lookup much more efficient.
I have a problem related to "dynamic ArrayLists". I have a List that contains usernames and their data. I want for every distinct username to create a single list that contains all data of this user. For example, I have an arraylist (username,tweet) that has: lefteris,"Plays ball", Kostas, "Plays basketball", lefteris, "Nice weather". And I want after that to create two lists. One list with kostas and his tweets and another with lefteris and its tweets (2 tweets). The parent arraylist may have 20 distinct usernames or more. How can I do that ?
I recommend you to use hashmap or hashset instead because if you need to store something in pairs, hashing is a perfect solution......
I'd go with the following data structure:
HashMap<String, ArrayList<String>>
Then you could manipulate a "dynamic" list of properties keyed to each name, if the properties are single items:
Lefteris->("Plays ball", "Nice weather",...)
Kostas->("Plays basketball",...)
If the properties are key-value pairs, do:
HashMap<String, HashMap<String, Object>>
Data looking like:
Lefteris->(Sport->"Plays ball", Weather->"Nice",...)
Kostas->(Sport->"basketball",...)
Since you parse the items from a file, you can do the following.
Create a map that contains the tweets associated to a particular username
Map<String,List<String>> userTweets = new HashMap<String,List<String>>();
Then, have a method to associate a tweet to certain user, verifying that it is already added in the map and adding it if it isn't.
public void addTweetToUser(String user, String tweet) {
if(userTweets.containsKey(user))
userTweets.get(user).add(tweet);
else {
List<String> newUserTweets = new LinkedList<String>();
newUserTweets.add(tweet);
userTweets.put(user, newUserTweets);
}
}
As a plus, you can improve this by creating an object UserTweet that contains:
public class UserTweet {
private String user;
private String tweet;
//Constructor, Setters & Getters or all of them
}
Then your addTweetToUser method can have an UserTweet parameter instead.
When you want to know the tweets for a certain user, you just obtain the corresponding list from the userTweets map. I alsomethods to remove tweets and/or remove users, just in case.
Several libraries add excellent collection-processing functionality to Java along the lines of what functional languages provide. One such library is Google Guava. Guava provides a MultiMap suitable for grouping things the way you want. There are also many utility methods, like MultiMaps.index(), which collects items from a list into a map by applying some function to the elements of the list to calculate a key. With such support, it only takes a few lines of code and one Function implementation (a closure in any other language) to solve your problem:
import com.google.common.base.Function;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multimaps;
import java.util.Arrays;
import java.util.List;
public class Tweets {
public static final int NAME = 0;
public static final int TWEET = 1;
public static void main(String[] args) {
List<String> namesAndTweets = Arrays.asList(
"lefteris", "Plays ball",
"Kostas", "Plays basketball",
"lefteris", "Nice weather");
List<List<String>> nameTweetPairs =
Lists.partition(namesAndTweets, 2);
Multimap<String, List<String>> namesAndTweetsByName =
Multimaps.index(nameTweetPairs, get(NAME));
Multimap<String, String> tweetsByName =
Multimaps.transformValues(namesAndTweetsByName, get(TWEET));
System.out.println(tweetsByName);
}
private static Function<List<String>, String> get(final int n) {
return new Function<List<String>, String>() {
#Override
public String apply(List<String> nameAndTweet) {
return nameAndTweet.get(n);
}
};
}
}
Outputs:
{lefteris=[Plays ball, Nice weather], Kostas=[Plays basketball]}
Update: To explain the code a bit more, there are three basic steps:
Take the list that has names and tweets all mixed together and use Lists.partition() to break it into pairs of (name, tweet).
Use MultiMaps.index() to build a MultiMap from the pairs, taking the name as the map key. This gives you a map where map keys are names and map values are the (name, tweet) pairs.
Use MultiMaps.transformValues() to reduce the map values from (name, tweet) pairs to just the tweets.
P.S. does anyone know if there's a built-in Function that does what my get() does? It seems like a useful Function that should be provided, but I can't find it anywhere.
I have a list of Order objects -
class Order {
Date date;
float amount;
String companyCode;
}
List<Order> orders = /* Initialize with list of order objects with valid data */
I have a list of Company objects -
class Company {
String name;
String code;
String address;
}
List<Company> companies = /* Initialize with list of company objects with valid data */
I need a to create a map of companyCode and name.
Is there some library that would allow me to write code like this (where BeanSearch is the hypothetical library class)?
Map<String, String> codeAndName = new HashMap<String, String>();
for(Order o: orders) {
codeAndName.put(o.getCompanyCode(),
BeanSearch.find(companies, "code", o.getCompanyCode).getName());
}
Alternatively is there another good way to do it?
http://commons.apache.org/collections/apidocs/org/apache/commons/collections/CollectionUtils.html should work for you right? Specifically you can use the find method