Java - divide Hashtable into lists - java

I have for example these pairs:
key => name:
"name" => "myname1"
"height" => "myheight1"
"name" => "myname2"
"height" => "myheight2"
Now if I put all of these into Hashtable, then second name and height keys, will overwrite first key name and height key names. I could add it into two different Hashtables, but before runtime, I don't know how many such pairs will be, so I would not know the number Hashtables I would need. So How Can I add these paris into the lists (without knowing before runtime exact number of such pairs (for example some times there could be three names keys and two height keys with their values etc.).
So when I add all these values, I need in a list to appear like this:
list<list<Object>> = [[{name=myname1, height=myheight1}], [{name=myname2, height=myheight2}]]
And for example if there would be different number of such pairs like two name and three height pairs:
list<list<Object>> = [[{name=myname1, height=myheight1}], [{name=myname2, height=myheight2}], [{height=myheight3}]]
Note: I get these pairs from xml file, so they are somewhat grouped, like every iteration I get one group of such pairs. For example
<main>
<group>
<name>myname1</name>
<height>myheight1</height>
</group>
<group>
<name>myname2</name>
<height>myheight2</height>
</group>
</main>
So how could I properly add such pairs into list, so keys would not overwrite each other and later I could get any pair I want from the list?

Why not just use a List:
List<Group> groups = new ArrayList<Group>();
where Group is defined as
public class Group {
private final String name;
private final int height;
public Group(String name, int height) {
this.name = name;
this.height = height;
}
// getters are left as an exercise to the reader
}
(you may need to use another type than int depending on the representation of height).
EDIT
Another option would be to use a dynamic language such as Groovy which make such tasks much easier (as you don't have to keep track of your XML document structure). Here's an example:
http://groovyconsole.appspot.com/edit/1445001

Generally, you can use a Map<String,List<String>> (or a Set for the second parameter, if you don't care about order).
More specifically, if your data is structured, so should your code and classes be. In this case, consider creating a Group class to keep your (semi-)structured data:
public class Group {
String name , height;
// constructor, getters, setters
}
and stuff objects into a List<Group> (or Set as per above). To retrieve the pairs, you would just iterate over the List:
List<Group> groups = ....
for ( Group g : groups ) {
System.out.println( "name=>" + g.getName() + ", height=>" + g.getHeight() );
}
Cheers,

As some other answers have said, you probably need to have your 'value' type as a List.
class MyClass {
....
Map<String, List<String>> myMap = new HashMap<String, List<String>>();
....
public void addPair(final String key, final String value) {
if (myMap.containsKey(key)) {
myMap.get(key).add(value);
} else {
final List<String> newList = new ArrayList<String>();
newList.add(value);
myMap.put(key, newList);
}
}
}

I would prefer to go with new class say
Class Entry
{
String name;
String height
}
And now add entries to List<Entry>

Related

Have to display the number of times an element has been added in my map JAVA

I've created a TreeMap with products.
And I want to count the number of times they repeat themselves, but have no clue what to code. Any hints? (I expect no code, just suggestions)
private static Map<Integer, String> shoppingCart() {
Map<Integer, String> result = new TreeMap<>();
result.put(1, "sausage");
result.put(2, "sausage");
result.put(3, "soup");
result.put(4, "egg");
result.put(5, "egg");
result.put(6, "tomato");
result.put(7, "sausage");
return result;
}
I was thinking about adding a counting variable, but still it doesn't fix the repeating problem.
Maybe not the best approach, but without modifying what you already have, you could use another map to store the products as keys and the quantity as the value for those keys:
Map<Integer, String> result = shoppingCart();
Map<String, Integer> productQuantities = new HashMap<>();
result.values().forEach(value ->
productQuantities.put(value,productQuantities.getOrDefault(value, 0) + 1));
To print the resulting map:
productQuantities.forEach((key, value) -> System.out.println(key + ":" + value));
I created a TreeMap with products, and i want to count the number of times they repeat themselves
Probably a different type of Map with keys representing items and values representing the corresponding count would be more handy. Something like:
NavigableMap<String, Integer> countByItem
Note: in order to access methods of the TreeMap like ceilingKey(), floorKey(), higherEntry(), lowerEntry(), etc. which are not defined in the Map interface you need to use NavigableMap as a type.
And it might make sense to make the item to be a custom object, instead of being a String. That would guard you from making typo, and it provides a possibility to gives useful behavior to Item
public class Item {
private int id;
private String name;
// constructor, getters, equals/hashCode, ect.
}
That's how map of items can be updated using Java 8 method merge(), which expects a key, a value and a function responsible for merging the old value and the new one:
NavigableMap<Item, Integer> countByItem = new TreeMap<>(Comparator.comparingInt(Item::getId));
countByItem.merge(new Item(1, "sausage"),1, Integer::sum);
countByItem.merge(new Item(1, "sausage"),1, Integer::sum);
countByItem.merge(new Item(2, "soup"),1, Integer::sum);
If you don't feel very comfortable with Java 8 functions, instead of merge() you can use combination of methods put() & getOrDefault():
Item sausage = new Item(1, "sausage");
countByItem.put(sausage, countByItem.getOrDefault(sausage, 0) + 1);
I can only guess at your goal. In your Map <Integer, String>, what does the Integer represent? Product number? Quantity? Sequence number? Something else?
If the Integer represents quantity, you have it backwards. It should be Map <String, Integer>. In a Map<X,Y>, the X represents the key. A Map allows fast lookup by the key. The Y is the value -- the information you want to find for a particular key, if the key is in the Map.
For example, if you want to add "sausage", you want to check if it is in the Map. If it isn't, put it into the Map with quantity 1. If it is, retrieve it and update the quantity.
If the Integer represents a sequence number (1st item, 2nd item, 3rd item, ...), you don't need a Map. Consider using an array or a data structure that preserves order, such as a List.
However, using an array or List still leaves you with the problem of how find how many of each item are in the list, when duplicates are allowed, as they are in your problem. To solve that, consider a Map<String, Integer> where the Integer (map value) is the quantity, and the String (map key) is the product name.
If I were doing this, I'd create classes to allow me to glue together related information. Here is part of a hypothetical example, which might be more realistic than you need:
public class Product {
private int upc; // product code, often represented with bar code
private Decimal price;
private String description;
private String shortDescription;
private ProductClass prodClass; // department, taxable, etc.
// etc. -- add needed fields, or remove irrelevant
// constructors, getters, setters,
Override .equals and .hashcode in Product. You use the UPC for those.
If you use implements Comparable<Product>, you have the possibility of using binary search, or a search tree.
public class Receipt {
private Decimal total;
private Decimal taxableTotal;
private Map <Product,Integer> shoppingCart; // Product, Quantity
// etc.
When each item is scanned, you can lookup the Product in the Map, and add it if not found, or update the quantity if found, as in the previous answers.

Java multikey map with no value

I would like to be able to query on each key with no value.
A1...n , B1...n are Strings.
I have Sets of Strings which I need to add into structure in order to be able to query on each String and get its group Strings.
For example : {A1, A2, A3} , {B1,B2,B3, B4....., Bn}
map.get(A1) --> return {A2,A3}
map.get(A2) --> return {A1,A3}
map.get(A3) --> return {A1,A2}
map.get(B1) --> return {B2,B3, B4...Bn}
map.get(B2) --> return {B1,B3, B4 ..Bn}
map.get(B3) --> return {B1,B2, B4...Bn}
etc...
Any recommendations which data structure should I use?
I suggest you make a map that maps an individual key to its entire group.
So, instead of what you wrote, this:
A1 -> {A1, A2, A3}
If you then have an operation such as 'list members of the group, but dont list yourself', just write that logic at that point (loop through the group and skip over yourself / use a stream().filter() operation to do this).
This way you save a ton of memory - each 'group' can simply be the same object. This also means that if you have mutable groups, you can just operate on the group (though, you'd have to take good care to update the map in tandem):
String[][] input = {
{"A1", "A2", "A3"},
{"B1", "B2"}};
public Map<String, SortedSet<String>> makeGroupsData() {
var out = new HashMap<String, List<String>>();
for (String[] group : input) {
SortedSet<String> groupSet = new TreeSet<>(Arrays.asList(group));
for (String g : group) out.put(g, groupSet);
}
return out;
}
// and then for operations:
/** Returns a list of all <em>other</em> members of the group */
public SortedSet<String> getGroupMembers(String key) {
var group = groups.get(key);
if (group == null) throw new IllegalArgumentException("unknown: " + key);
var out = new TreeSet<String>(group);
out.remove(key);
return out;
}
public int getGroupSize(String key) {
Set<String> group = groups.get(key);
return group == null ? 0 : group.size();
}
You get the drift - I'm not sure if SortedSet is right for you here, for example. The point is, this means there is only 1 object (one TreeSet) for one group, and the map just links each individual member to the same one group object.
NB: Big caveat: This assumes any given string cannot be a member of more than one group, and this code does not check for it. You may want to (the .put method returns the previous mapping, so all you have to do is check if the .put method returns a non-null value; if it does, throw an IllegalArgumentException).

Group by multiple values

For example, I have a list of students with their semesters and subjects.
Subject Semester Attendee
---------------------------------
ITB001 1 John
ITB001 1 Bob
ITB001 1 Mickey
ITB001 2 Jenny
ITB001 2 James
MKB114 1 John
MKB114 1 Erica
When I need to group them by one value with Stream api, I can make something like that;
Map<String, List<Student>> studlistGrouped =
studlist.stream().collect(Collectors.groupingBy(w -> w.getSubject()));
and it does work. However, when I want to group them by two or more values (like the Sql handles), I cannot figure out how I need to do.
One approach is to group by a class that holds all the fields you want to group by. This class must implement hashCode and equals using all these fields in order to work as expected.
In your example this would be:
public class SubjectAndSemester {
private final String subject;
private final int semester; // an enum sounds better
public SubjectAndSemester(String subject, int semester) {
this.subject = subject;
this.semester = semester;
}
// TODO getters and setters, hashCode and equals using both fields
}
Then, you should create this method in your Student class:
public SubjectAndSemester bySubjectAndSemester() {
return new SubjectAndSemester(subject, semester);
}
Now, you can group as follows:
Map<SubjectAndSemester, List<Student>> studlistGrouped = studlist.stream()
.collect(Collectors.groupingBy(Student::bySubjectAndSemester));
This creates a one level grouping of students.
There's another option that doesn't require you to use a Tuple or to create a new class to group by. I mean you can have an n-level grouping of students, by using downstream groupingBy collectors:
Map<String, Map<Integer, List<Student>>> studlistGrouped = studlist.stream()
.collect(Collectors.groupingBy(Student::getSubject,
Collectors.groupingBy(Student::getSemester)));
This creates a 2-level grouping, so the returned structure is now a map of maps of lists. If you can live with this, even for grouping of more levels, then you can avoid creating a class that acts as the key of the map.
You could create a calculated field that is combination of the two columns
Map<String, List<Student>> studlistGrouped =
studlist.stream().collect(Collectors.groupingBy(w -> w.getSubject() + "-" w.getAttendee()));
EDIT:
Another more "appropriate" solution: Accoridng to this blog post , you need to define a Tuple class that can hold the two columns:
class Tuple {
String subject;
String attendee;
}
Map<String, List<Student>> studlistGrouped =
studlist.stream().collect(Collectors.groupingBy(w -> new Tuple(w.getSubject(), w.getAttendee())));

Get key from HashMap in Android by position or index

I have:
public static HashMap<String, String> CHILD_NAME_DOB = new HashMap<>();
Suppose the values in CHILD_NAME_DOB are:
<adam,15121990>
<roy,01051995>
<neha,05091992>
<alisha,11051992>
I am trying to fetch the last key element from CHILD_NAME_DOB. That is, I want to fetch key alisha from the example above to temporary String name.
Also I want to know on how to fetch data by index.
Eg.: if int index = 2 , I want key "Neha" in String name
TIA.
Edit: DateOfBirth value (value data in CHILD_NAME_DOB) is dynamic and is unknown. So THIS LINK is not what I want.
Single line solution:
First note that the Java HashMap does not guarantee the order of entries. So each time you iterate over a HashMap, entries appear in different positions. You will need LinkedHashMap that guarantees the predictable iteration order.
Map<String, String> CHILD_NAME_DOB = new LinkedHashMap<>();
Get the key by index:
key = (new ArrayList<>(CHILD_NAME_DOB.keySet())).get(index)
Get the value by index:
CHILD_NAME_DOB.get(key)
Thanks to #Pentium10 for this answer.
And I little modified it according to my need.
String key="default";
Iterator myVeryOwnIterator = CHILD_NAME_DOB.keySet().iterator();
while(myVeryOwnIterator.hasNext()) {
key=(String)myVeryOwnIterator.next();
//String value=(String)meMap.get(key);
}
Toast.makeText(viewEnterChildExp.getContext(), "Key: "+key , Toast.LENGTH_LONG).show();
I'm getting the last key element by this.
I'll update as soon I also get to find an easy way to key by index.
This way to get key....
public static String getHashMapKeyFromIndex(HashMap hashMap, int index){
String key = null;
HashMap <String,Object> hs = hashMap;
int pos=0;
for(Map.Entry<String, Object> entry : hs.entrySet())
{
if(index==pos){
key=entry.getKey();
}
pos++;
}
return key;
}
You can also use an ArrayMap instead of a HashMap. To get the value by index use:
ArrayMap.valueAt(index);
To get the Key at an index use:
ArrayMap.keyAt(index);
Fetching the "last" key and fetch by index is not supported by HashMap. You can use a LinkedHashMap and lookup the element with index 2 (or the last element) by iterating over it. But this will be a O(n) operation.
I suggest you use a List<Pair<String, String>> if the order of the keys/values is important to you and you wish to do index based lookup.
If both key based and index based lookup is important to you, you could use a combined data structure that consists of both a List and a HashMap, but note that removal of elements will be O(n).
You can create a class Child
public class Child(){
private String name;
private String number;
....
}
and then put this object in a List
public static List<Child> CHILD_NAME_DOB = new ArrayList<Child>(); // using LinkedList would defeat the purpose
in this way you can invoke the method get(int index), that returns the element at the specified position in this list.
In your example
<adam,15121990>
<roy,01051995>
<neha,05091992>
<alisha,11051992>
invoking CHILD_NAME_DOB.get(2) you'll get <neha,05091992>(as Child object)
HashMap does not have a concept of ordering, so getting the n-th entry does not make sense. You could use a TreeMap instead, which is ordered on its keys.
However, you should reconsider your model as you seem to have conflicting interests. On the one hand, accessing by index is typical for Lists, whereas accessing by key is typical for Maps. I'm not sure in which situation you'd want to do both.
If you really want to do both index and key accessing, you could write your own data structure that stores the data in a list combined with a mapping from key to index and vice versa. I would recommend against this, but if that's really what you want, then I think that's the best solution.
I know it is not the best solution, but what about this solution (pseudocode!). Just combine List and Map in one class.
public class UserBirthday {
private List<String> names = new ArrayList<>();
private Map<String, String> CHILD_NAME_DOB = new HashMap<String, String>();
public void add(String name, String bd) {
if (!CHILD_NAME_DOB.containsKey(name)) {
names.add(name);
}
CHILD_NAME_DOB.put(name, bd);
}
public String getByName(String name) {
return CHILD_NAME_DOB.get(name);
}
public String getByIndex(int index) {
return getByName(names.get(index)); // TODO: range test
}
public static void main(String[] args) {
UserBirthday ub = new UserBirthday();
ub.add("dit", "12345678");
ub.add("lea", "234239423");
ub.add("alex", "43534534");
ub.add("ted", "099098790");
System.out.println(ub.getByIndex(2));
System.out.println(ub.getByName("alex"));
}
}
You may get some problems if you remove an entry, but it should be just a suggestion.
for (String key : hmList.keySet()) {
String value = hmList.get(key);
Log.e("HashMap values", "key=" + key + " ,value=" + value);
}

Linking two hashmaps via searching or is there a better way?

I have a hashmap for orders and another one for orderitems. A method which puts the data into the hashmaps is executed like this:
// THIS ONE ADDS THE ORDERS
// (int orderNumber, String Datum, String salesperson, int customernumber)
mainController.addBestallning(500, "2012/01/01", "Hendrik Gustafsson", 1001);
// THIS ONE ADDS THE ORDERED ITEMS
// (int orderNumber, int linePos, Artikel product, int amount, double price)
mainController.addBestallningsOrderRad(500, 1, mainController.getAllaArtiklar().get(101), 5, 100.00);
Once I find an order by its ordernumber, how do I find the ordereditems?
The only link I have now is the ordernumber, which I save in orderitemshashmap, so I assume some sort of iteration needs to take place, find the matches and return the results.
I tried doing this and got it to work, but only under condition that all of the orderitem positions are also unique.
So, if I was to add another order like this:
mainController.addBestallning(501, "2011/05/02", "Sven Karmageddon", 1002);
mainController.addBestallningsOrderRad(501, 1, mainController.getAllaArtiklar().get(101), 5, 100.00);
I could not find the orderitems for order 501.
Here is what I tried so far. Made a method to find all orders of a customer:
public HashMap<Integer, Bestallning> getAllaKundOrdrar() {
HashMap<Integer, Bestallning> allaKundOrderHashMap = new HashMap<>();
//iterate through all orders
//find the ones which belong to customerid
//place them in allaKundOrderHashMap
//return allaKundOrderHashMap
Iterator iter = tmpBestallningsregister.getAllaBestallningar().keySet().iterator();
while (iter.hasNext()) {
Integer key = (Integer) iter.next();
//String value = (String) controller.getAllaKunder().get(key).getKundNamn();
if ((customerNrToFindOrdersFor) == getAllaBestallningar().get(key).getKundNr()) {
//found an order for this customer, putting it in the hashmap
allaKundOrderHashMap.put(key, getAllaBestallningar().get(key));
}
}
return allaKundOrderHashMap;
}
A method to find all ordereditems from all customers (100% wrong to search like this, I know) and get the ones beloning to a specific order:
//RETURN OF ORDERRADERS HASHMAP FOR SPECIFIC ORDER VIA ORDERREGISTER
public HashMap<Integer, BestallningsOrderRad> getAllaBestallningsBestallningsOrderRader() {
HashMap<Integer, BestallningsOrderRad> allaBestallningsOrderRaderHashMap = new HashMap<>();
//iterate through all orderrader
//find the ones which belong to orderid
//place them in allaKundOrderRaderHashMap
//return allaKundOrderRaderHashMap
Iterator iter = tmpBestallningsregister.getAllaBestallningsOrderRader().keySet().iterator();
while (iter.hasNext()) {
Integer key = (Integer) iter.next();
if ((orderNrToFindOrderRaderFor) == tmpBestallningsregister.getAllaBestallningsOrderRader().get(key).getBestallningsNr()) {
//found an orderrad for this order, putting it in the hashmap
//allaBestallningsOrderRaderHashMap.put(key, getAllaBestallningsOrderRader().get(key));
allaBestallningsOrderRaderHashMap.put(key, getAllaBestallningsOrderRader().get(key));
}
}
return allaBestallningsOrderRaderHashMap;
}
Anyone care to tell me what is it that I am doing wrong?
I've been at this for 20 hours straight...
Don't use two different Map, but only one. What you need is to properly define a Order class that holds all the order data (including suborders, which are sheldom used outside the order) and use just a Map<Integer,Order>
If you insist in having two separate Maps, the second uses the same id but stores a List (it looks like you want it ordered) of suborders.
private Map<Integer, Order> orders;
private Map<Integer, List<SubOrder> suborders;
Stopping a moment before beginning to code and thinking the more appropiate data structures will usually save you a lot of "hell" later.

Categories