Implement data structure and query for params - java

I want to create this data stricture:
Map<String, Country> table = new HashMap<>();
table.put("AF", new Country("Afghanistan", "AFG", "004"));
// 300 more
class Country {
private String country;
private String alpha3;
private String number;
public Country(String country, String alpha3, String number) {
this.country = country;
this.alpha3 = alpha3;
this.number = number;
}
}
Second way:
Map<String, Country> table = new HashMap<>();
Table<String, String, Integer> table2 = HashBasedTable.create();
table.put("AF", HashBasedTable.create().put("Afghanistan", "AFG", "004"));
// 300 more
I want to search values into the data structure values based on keys that I send:
numeric code from country name
country code from country name
alpha3 code from country name
What is the bast way to implement this search into the second data structure?

HashMap contains a method called forEach(BiConsumer<? super K,? super V> action) which can be used to iterate through each key-value pair in your HashMap.
Therefore, using a lambda you can do something like this:
//Search a HashMap of employee ID/name records by name, for their ID number.
public int getID(HashMap<Integer, String> employees, String name) {
//Iterate through every key-value pair, performing the lambda's operation on it.
employees.forEach((Integer j, String k) -> {
if(k.equals(name)) return Integer.valueOf(j); //If the value matches the name, return the ID
}
return -1;
}

Related

JAVA - Reading CSV file based on another CSV file

I am struggling with making this work properly. So I have two CSV Files.
And this One
So the main thing is. I have SearchTerms in 2nd CSV. In first CSV I have SearchTerms also, which should work as a "tag".
What I need is to get product ids from first CSV saved to a List < String > based on, what searchterm from 2nd csv is used. So when Akt (pan) is used, one set of List of IDS is exported. If there is more Akt (pan) sets of ids, they are saved to one list as array I guess.
I tried to read it with CSVloaders and searching it with lookup() method
private final Map<List<String>, Comparison> data = Maps.newHashMap();
public Comparison lookup(String searchTerm) {
return data.get(searchTerm);
}
Where Comparison Class is
public class Comparison {
#Parsed(field = "ProductId1")
private String productId1;
#Parsed(field = "ProductId2")
private String productId2;
#Parsed(field = "ProductId3")
private String productId3;
#Parsed(field = "SearchTerm")
private String SearchTerm;
public String getProductId1() {
return productId1;
}
public String getProductId2(){
return productId2;
}
public String getProductId3(){
return productId3;
}
public List<String> getProductIds(){
List<String> ids = new ArrayList<>();
Collections.addAll(ids, productId1, productId2, productId3);
return ids;
}
}
My solution was bad. I was getting NullPointerException constantly whenever I tried to use lookup() method.
Do you have any ideas how to make this work? Thank oyu
Problem is with the data type of key in your HashMap. It should be a string as per your data, not a List<>.
private final Map<String, Comparison> data = Maps.newHashMap();
public Comparison lookup(String searchTerm) {
return data.get(searchTerm);
}
Then the returning object(typed Comparison) would have the all products Ids for the given search Term.

How to loop through List of objects of and compare value in every object in Java?

So I have an object that stores Country, Year and Value
I then have List<Object> which will store objects for each year
Each List<object> consist of data of each range of years
I'd like to loop through any number of countries, let's say 4, and then in each object I want to retrieve the value of each year.
For example
Let's assume we want our object to return data from year 1980 to 1983
So my code looks like this:
//Example of our data
[AF, 19199437988.8023, 1980, AF, 20050189881.6659, 1981, AF, 20046334303.9661, 1982, AF, 20536542736.7297, 1983]
[UA, 90615023323.7353, 1980, UA, 131805126738.287, 1981, UA, 181334417615.413, 1982, UA, 175781379051.433, 1983]
[ID, 861933968740.332, 1980, ID, 890487074595.966, 1981, ID, 912524136718.019, 1982, ID, 917869913364.916, 1983]
[AU, 1339539063150.01, 1980, AU, 1454675479665.84, 1981, AU, 1563950959269.52, 1982, AU, 1537477830480.51, 1983]
// each value is a country. 'AF' stands for AFghanistan, and so on...
String[] countries = new String[] {
"AF", "UA", "ID", "AU"
};
for (int i = 0; i < countries.length; ++i) {
if (countries.get(i).getYear() == "I don't know how to retrieve each year here.") {
System.out.println(countries.get(i));
}
}
Is this possible? If so then how can I get it?
Thanks
After you had declared your POJO:
public class MyObject {
Integer year;
String country;
BigDecimal value;
public MyObject(Integer year, String country, BigDecimal value) {
super();
this.year = year;
this.country = country;
this.value = value;
}
//getter/setter
}
And after you had created and added object values in the list:
...
MyObject af1 = new MyObject(1980,"AF",new BigDecimal("19199437988.8023"));
MyObject af2 = new MyObject(1981,"AF",new BigDecimal("20050189881.6659"));
MyObject af3 = new MyObject(1982,"AF",new BigDecimal("20046334303.9661"));
MyObject af4 = new MyObject(1983,"AF",new BigDecimal("20536542736.7297"));
...
MyObject ua1 = new MyObject(1980,"UA",new BigDecimal("90615023323.7353"));
MyObject ua2 = new MyObject(1981,"UA",new BigDecimal("131805126738.287"));
MyObject ua3 = new MyObject(1982,"UA",new BigDecimal("181334417615.413"));
MyObject ua4 = new MyObject(1983,"UA",new BigDecimal("175781379051.433"));
...
List<MyObject> completeList = Arrays.asList(af1,af2,af3,af4,ua1,ua2,ua3,ua4,id1,id2,id3,id4,au1,au2,au3,au4);
You could use java8 streams in order to get what you're looking for:
1) Filter the original list in order to get only object with 1980<=year<=1983:
int yearFrom =1980;
int yearTo =1983;
List<MyObject> filteredList = completeList.stream().filter(s -> s.getYear()>=yearFrom && s.getYear()<=yearTo).collect(Collectors.toList());
2) Get a map(year,value) for each country i.e. for AF:
Map<Integer, BigDecimal> map = filteredList.stream()
.filter(s -> ("AF").equals(s.getCountry()))
.collect(
Collectors.toMap(
s -> ((MyObject)s).getYear(),
s -> ((MyObject)s).getValue())
);
System.out.println("(year,value) for AF Country are:");
System.out.println(map);
You could get a map like this: Map(country,Map(year,value)), in the following way:
Map<String, Map<Integer, BigDecimal>> mapCountries = filteredList.stream()
.collect(Collectors.groupingBy(MyObject::getCountry,
Collectors.toMap(
s -> s.getYear(),
s -> s.getValue()))
);
Then you can iterate through the countries and do mapCountries.get(country) in order to obtain the Map(year,value) for each country.
Based on the answer:
"This is just to illustrate my problem. I don't have such structure in my real application. The data above the code is just an example of how my data looks like"
I would suggest you to create a POJO that stores that data:
class POJO { //add nice name to class!
private Map<Integer, Double> yearValueMap;
private String countryName;
// add setter and getter here.
add(Integer year, Double value) {
this.yearValueMap.put(year, value);
}
}
And then you create objects that are storing that data, something like:
POJO pojo = new POJO();
pojo.add(1890, 14.31);
pojo.setCountryName("Italy");
and so you have objects containing this data. Then you have a
List<POJO> countryInformation;
that you can iterate.
Hope it helps and that I did not make any typo, I've coded it here.
Editing for adding the code in the comment:
List <Object> myData = receiveDataFromApi();
for(int i = 0; i < myData.size(); i+=3) {
...
pojo.setCountryName= myData.get(i);
pojo.add(myData.get(i+2), myData.get(i+1));
...
}
I've used the POJO just for making the point, but you should use your own model for that.

How to write my own comparator class in java?

I didn't find proper solution for the below scenario. I have employee names and location. In each location many employees can work.
Example: assume that employee names are unique so I consider it as a key and value as location.
TreeMap<String,String> t=new TreeMap<String,String>();
t.put(mike, Houston);
t.put(arian, Houston);
t.put(John, Atlanta);
Well my scenario is i have to write my own comparator where location is sorted first and when there are multiple locations of same name then they need to be sorted by employees. Any kind of help is appreciated.
you need a structure, and compareTo:
public class EmpLoc implements Comparable<EmpLoc> {
String employee;
String location;
public EmpLoc (String _employee, String _location)
{
employee=_employee;
location=_location;
}
#Override
public int compareTo(EmpLoc other)
{
int last = this.location.compareTo(other.location);
return last == 0 ? this.employee.compareTo(other.employee) : last;
}
}
The problem is in your data structure. TreeMap ensure your keys are always sorted in an order, but your key doesn't have full information you need to sort. Instead what you need is probably
TreeSet<Employee> employees = new TreeSet<>(employeeComparator);
where Employee is:
public class Employee {
private String name;
private String location;
/* getters & setters omitted */
}
Now you can create a comparator for Employee
You can use similar structure:
Map<String, List<String>> map = new TreeMap<>(<your_own_comparator_for_locations_or_default_one>);
This is Multimap, and this is implementation by conventional means, but also there are third-party implementation, e.g. Guava. Guava has some sorted, synchronized and immutable implementations of multimaps, you can use them by default or to see how to do some things.
You can put values like below:
public void putEmployees(String location, String employee) {
List<String> employees = map.get(location);
if (employee == null) {
employees = new ArrayList<>();
}
employees.add(employee);
Collections.sort(employees, <your_own_comparator_for_employees_or_default_one>);
map.put(location, employees);
}

Multi Key Hashmap

Recently I had an interview to save the huge count of employee details in DS.
I gave the solution as Hashmap with emp Id as key.
The follow up question was if the user wants to search based on name how to implement it. I suggested to use emp name as key and save all the employees with same name as Arraylist.
The next follow up question was tricky, need to create ONE map where user can search based on emp Id or emp name. How to implement this in map?
Implement it in memory efficient way.
This is a dirty solution (yes--very dirty, never do it on production!), but it will work if keys are of different types and one is not subtype of another (e.g. long and String). Put every employee by both keys, and get by provided key, either id or name:
Map<?, List<Employee>> map = new HashMap<>();
public void putEmployee(Employee e) {
map.put(e.id, Arrays.asList(e)); // put by id
if (!map.containsKey(e.name)) {
map.put(e.name, new ArrayList<>());
}
map.get(e.name).add(e); // put by name
}
public Employee getById(long id) {
return map.containsKey(id) ? map.get(id).get(0) : null;
}
public List<Employee> getByName(String name) {
return map.containsKey(name) ? map.get(name) : Collections.emptyList();
}
In production code, I'd use two separate maps or custom dictionary class.
I have come up with a solution. Please post your suggestions.
Step 1: Form the hashmap with emp id as key and emp object as value.
Step 2: For the same name create a list of emp id who matches the name Ex: name = 'XYZ' id={101,102,103,...}
Step 3: Insert this name as key and arraylist as value to the same map
Here we are not storing complete employee detail twice. Just trying to maintain a relationship between name and id. So comparatively it could be memory efficient.
This is a pretty easy question to answer: Just convert the IDs to Strings and store employees twice - once under the name and again under the id-as-string.
Your idea of using a List as the value is fine - for IDs, the list would be of size 1.
Note that it would be better to use two maps, because you only ever have one employee per ID and you wouldn't have to deal with a list of size 1 as a degenerate case, so:
Map<Integer, Employee> employeesById;
Map<String, Set<Employee>> employeesByName;
Especially note that you wouldn't use less memory by using just one map. In fact, you would use more memory than storing employees in separate maps for ID keys and name keys.
One way to do this would be to create a Key object that can be searched by either the name or the id:
public enum KeyType {
ID, NAME;
}
public class SearchKey {
private KeyType keyType;
private String value;
// constructor and getters snipped for brevity's sake
#Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
SearchKey searchKey = (SearchKey) o;
return keyType == searchKey.keyType && value.equals(searchKey.value);
}
#Override
public int hashCode() {
int result = keyType.hashCode();
result = 31 * result + value.hashCode();
return result;
}
public class Directory {
private Map<SearchKey, Set<Employee>> directory = new HashMap<>();
public void addEmployee(Employee e) {
// Add search by id
directory.put
(new SearchKey(KeyType.ID, e.getId()), Collections.singleton(e));
// Add search by name
SearchKey searchByName = new SearchKey(KeyType.NAME, e.getName());
Set<Employee> employees = directory.get(searchByName);
if (employees == null) {
employees = new HashSet<>();
directory.put(searchByName, employees);
}
employees.add(e);
}
public Employee getById (String id) {
// Assume that the ID is unique
return directory.get(new SearchKey(KeyType.ID, id)).iterator().next();
}
public Set<Employee> getByName (String name) {
return directory.get(new SearchKey(KeyType.NAME, name));
}
}

Key with multiple values to be accessed in two ways JAVA

I'm reading different keys and their corresponding keys from a textfile.
I want to create a hashmap that takes in the keys and their corresponding keys. It needs to be accesible in both ways.
How do I do this?
I've managed to do it but it works only for the left side.
As every country has only a few members I would do it with a map and just implement a method to update the state of each country in a pair of neighbours. If it was, however, a dense structure, i.e. every element had nearly all other as neighbours, I would recommend using an indicator matrix: rows and columns are countries and a true value on the intersection defines they are neighbours. But here goes the first solution, with a map:
public class Countries
{
private final Map<String, Set<String>> countries = new HashMap<String, Set<String>>();
public void addCountry(#NotNull String name) {
addNeighbourPair(name, null);
}
public void addNeighbourPair(#NotNull String first, String second) {
if (!hasCountry(first)) {
countries.put(first, new HashSet<String>());
}
if (second != null) {
if (!hasCountry(second)) {
countries.put(second, new HashSet<String>());
}
countries.get(first).add(second);
countries.get(second).add(first);
}
}
public boolean hasCountry(String name) {
return countries.containsKey(name);
}
public Set<String> getNeighbours(String name) {
return countries.get(name);
}
/*
* the correctness of this loader is validated only with respect
* to using the Countries class :)
*/
public static Countries fromFile(String borders) {
Countries countries = new Countries();
Scanner bordersload = new Scanner(new File(borders));
while (bordersload.hasNextLine()) {
String line = bordersload.nextLine();
String[] values=line.split(" : |:|: | :");
String key=String.valueOf(values[0]);
String key1=String.valueOf(values[1]);
countries.addNeighbourPair(key, key1);
}
bordersload.close();
return countries;
}
}
Usage:
Countries countries = Countries.fromFile("path/to/file");
Each map entry should contain a set of countries that borders it. Every country should have it's own Map entry
You can use a map of <String, Set<String>>
Where key is a country and the value is a set of neighbors. For each line, check if the country exists in the map, if it does, update its neighbor (add new neighbor in the set). If doesn't, create a new entry with value.

Categories