How to loop though List<Pair>> iterator - java

I have a method that loops through a map containing a <Key, <List<Pair>>. How would I loop through this and get all results? As this list contains multiple currencies. I don't want the different type of currency amounts to add together. My attempt is below, it seems to not be picking up all the results

Assuming you have a Map which contains all of your data you could do something like this:
Iterator<Entry<Date, List<Pair<BigDecimal, Currency>>>> itr = source.entrySet().iterator();
Now that you have this Iterator, you can convert it to an Element iterator:
Iterator<Element> eItr = new Iterator<Element> {
int index = 0;
Iterator<Entry<Date, List<Pair<BigDecimal, Currency>>>> itr = source.entrySet().iterator();
Entry<Date, List<Pair<BigDecimal, Currency>>> current = itr.hasNext()? itr.next() : null;
#Override
public boolean hasNext() {
return current != null;
}
#Override
public Element next() {
try {
return new Element(current.getKey(), current.getValue().get(index).getFirst(), current.getValue().get(index).getSeccond());
} finally {
index++;
if (index >= current.getValue().size()) {
index = 0;
current = itr.hasNext()? itr.next() : null;
}
}
}
}

Map<Date, List<Pair<BigDecimal, Currency>>> map = ...
map.forEach((date, list) -> {
list.forEach(pair -> {
// business logic here
});
});
This will loop through the map, and in turn loop through every list. It is unclear what you are trying to achieve, but hopefully this will point you in the right direction.
EDIT (without Java 8)
for (Map.Entry<Date, List<Pair<BigDecimal, Currency>>> entry : map.entrySet()) {
Date date = entry.getKey();
List<Pair<BigDecimal, Currency>> list = entry.getValue();
for (Pair<BigDecimal, Currency> pair : list) {
BigDecimal bd = pair.getKey();
Currency currency = pair.getValue();
//business logic here
}
}
It's slightly less elegant (I guess that's a reason to upgrade to Java 8), but functionally equivalent to the first solution (with slight scope/variable name differences). the for (T t : Iterable<T>) syntax was introduced in Java 5 with the Iterable interface, so this should work for practically everyone.
EDIT (If specifically using Iterator)
while (source.hasNext()) {
Map.Entry<Date, List<Pair<BigDecimal, Currency>>> entry = source.next();
Date date = entry.getKey();
List<Pair<BigDecimal, Currency>> list = entry.getValue();
for (Pair<BigDecimal, Currency> pair : list) {
BigDecimal bd = pair.getKey();
Currency currency = pair.getValue();
//business logic here
}
}

Related

Unflatten a HashMap of values

I currently have a Map of key value pairs in the format of
a.b.c: value1
e.f: value2
g: [
g.h: nested_value1
g.i: nested_value2
]
and I need to 'unflatten' this to a new Map in a nested structure -
a:
b:
c: value1
e:
f: value2
g: [
h: nested_value1
i: nested_value2
]
My current attempt doesn't get very far, and throws a ConcurrentModificationException
private static Map<String, Object> unflatten(Map<String, Object> flattened) {
Map<String, Object> unflattened = new HashMap<>();
for (String key : flattened.keySet()) {
doUnflatten(flattened, unflattened, key, flattened.get(key));
}
return unflattened;
}
private static Map<String, Object> doUnflatten(
Map<String, Object> flattened,
Map<String, Object> unflattened,
String key,
Object value) {
String[] parts = StringUtils.split(key, '.');
for (int i = 0; i < parts.length; i++) {
String part = parts[i];
Object current = flattened.get(part);
if (i == (parts.length - 1)) {
unflattened.put(part, value);
} else if (current == null) {
if ((current = unflattened.get(part)) == null) {
current = new HashMap<>();
}
unflattened.put(part, current);
unflattened = (Map<String, Object>) current;
} else if (current instanceof Map) {
unflattened.put(part, current);
unflattened = (Map<String, Object>) current;
}
}
return unflattened;
}
Am I missing something obvious here? One solution is to use a library like JsonFlattener - the only issue is this would involve converting back and forward between JSON alot.
Edit: Thanks for the pointers - I am half way there, one thing I forgot to mention was it also needs to unflatten a collection of HashMaps
Your error comes because you iterate the key set and then change the map, not through the iterator.
The iterators returned by all of this class's "collection view
methods" are fail-fast: if the map is structurally modified at any
time after the iterator is created, in any way except through the
iterator's own remove method, the iterator will throw a
ConcurrentModificationException. Thus, in the face of concurrent
modification, the iterator fails quickly and cleanly, rather than
risking arbitrary, non-deterministic behavior at an undetermined time
in the future.
You could get around this by using a new map.
The problem with your implementation is that you are writing the output into the same Map that you use for the input, which causes ConcurrentModificationException.
Implementation becomes straightforward with a separate Map for output:
Map<String,Object> unflattened = new HashMap<>();
for (Map.Entry<String,Object> e : flattened.entrySet()) {
String[] parts = StringUtils.split(e.getKey(), ".");
// Find the map to be used as a destination for put(...)
Map<String,Object> dest = unflattened;
for (int i = 0 ; i != parts.length-1 ; i++) {
Object tmp = dest.get(parts[i]);
if (tmp == null) {
// We did not see this branch yet
Map<String,Object> next = new HashMap<>();
dest.put(parts[i], next);
dest = next;
continue;
}
if (!(temp instanceof Map)) {
throw new IllegalStateException();
}
dest = (Map<String,Object>)temp;
}
// Put the entry into the destination Map<>
dest.put(parts[parts.length-1], e.getValue());
}
Note that the process of "unflattening" may fail when the initial map describes an inconsistent hierarchy, for example, one with a branch and a leaf having the same name:
"a.b.c" -> "x" // OK: "a.b" is a branch
"a.b.d" -> "y" // OK: "a.b" is a branch
"a.b" -> "z" // Error: "a.b" is a leaf
Create a new Map instance for your result instead of attempting to reuse the current one. Also, send in the map value, so it doesn't need to be extracted:
private static Map<String, Object> unflatten(Map<String, Object> flattened) {
Map<String, Object> unflattened = new HashMap<>();
for (String key : flattened.keySet()) {
doUnflatten(unflattened, key, flattened.get(key));
}
return unflattened;
}
This also prevents the original keys from being present in the resulting map.
The above also requires a slight rewrite of the doUnflatten method:
private static void doUnflatten(Map<String, Object> current, String key,
Object originalValue) {
String[] parts = StringUtils.split(key, ".");
for (int i = 0; i < parts.length; i++) {
String part = parts[i];
if (i == (parts.length - 1)) {
current.put(part, originalValue);
return;
}
Map<String, Object> nestedMap = (Map<String, Object>) current.get(part);
if (nestedMap == null) {
nestedMap = new HashMap<>();
current.put(part, nestedMap);
}
current = nestedMap;
}
}
Couple of notes: There's no need to return the map from the method. Divide the loop into two distinct cases: Either the value should be written to the map, or a nested map should be created or retrieved.
The simplest solution is to replace line
for (String key : flattened.keySet()) {
to
for (String key : new ArrayList<>(flattened.keySet())) {
but for large data amount it can be not very effective from performance perspective.

Deleting the treeMap entries with null values

I am attempting to remove all the nulls, but if the last key's treeSet is null then it remains there. So I was thinking how to delete the last entry if it is null. Since this is a treeMap I thought that I can obtain the last element by accessing it with tm.lastKey() but that method does not seem to exist. So this question is twofold. First, is there a way to delete all the nulls including the last one and the second is, where is the .lastKey() method?
public class Timing {
private static Map<String, SortedSet> tm = new TreeMap<String, SortedSet>();
public static Map manipulate() {
SortedSet ss = new TreeSet();
ss.add("APPL");
ss.add("VOD");
ss.add("MSFT");
tm.put("2019-09-18",null);
tm.put("2019-09-21",ss);
tm.put("2019-09-22", null);
tm.put("2019-09-20",ss);
tm.put("2019-09-19", null);
tm.put("2019-09-23",null);
return tm;
}
public static void printMap() {
for (String s: tm.keySet()) {
System.out.println(s + ": " + tm.get(s));
}
}
// Will delete all but the last one
public static void deleteNull() {
Set set = tm.entrySet();
Iterator i = set.iterator();
Map.Entry me = (Map.Entry) i.next();
// there is no tm.lastKey()??
while(i.hasNext()) {
if (me.getValue() == null) {
i.remove();
}
me = (Map.Entry) i.next();
}
}
}
To remove all entries with a value of null from your map you can replace the deleteNull method with
tm.values().removeIf(Objects::isNull);
A Java TreeMap does specify a lastKey() method. You can see it in the Java-Doc for TreeMap.
The problem is, you can not access the method because you are hiding the real type of your map to your method. You can see it here:
private static Map<String, SortedSet> tm = new TreeMap<String, SortedSet>();
From this, your method only knows that tm is a Map object and those do not have the lastKey() method. Change Map to TreeMap or do a cast inside your method, then it will work.
Alternative 1:
private static TreeMap<String, SortedSet> tm = new TreeMap<String, SortedSet>();
Alternative 2:
public String lastKey() {
if (tm instanceof TreeMap<?, ?>) {
return ((TreeMap<String, SortedSet>) tm).lastKey();
} else {
// Error!
}
}
The absolute simplest way to do this would be to run check the iterator one more time after the while loop ends, like so:
while(i.hasNext()) {
if (me.getValue() == null) {
i.remove();
}
me = (Map.Entry) i.next();
}
if (me.getValue() == null) {
i.remove();
}
me = (Map.Entry) i.next();
This way you'll catch the last value.
However, you can use the key set similar to how you printed the the map.
Set<String> keySet = tm.keySet();
for(int ndx = 0; ndx < keySet.size(); ndx++){
String key = keySet.get(ndx);
if(tm.get(key) == null){
tm.remove(key);
}
}

How to calculate the sum of values of different hashmaps with the same key?

So I have this hashmap named "hm" which produces the following output(NOTE:
this is just a selection) :
{1=35, 2=52, 3=61, 4=68, 5=68, 6=70, 7=70, 8=70, 9=70, 10=72, 11=72}
{1=35, 2=52, 3=61, 4=68, 5=70, 6=70, 7=70, 8=68, 9=72, 10=72, 11=72}
{1=35, 2=52, 3=61, 4=68, 5=68, 6=70, 7=70, 8=70, 9=72, 10=72, 11=72}
This output was created with the following code(NOTE : the rest of the class code is not shown here) :
private int scores;
HashMap<Integer,Integer> hm = new HashMap<>();
for (int i = 0; i < fileLines.length(); i++) {
char character = fileLines.charAt(i);
this.scores = character;
int position = i +1;
hm.put(position,this.scores);
}
System.out.println(hm);
What I am trying to do is put all these hashmaps together into one hashmap with as value the sum of the values per key. I am familiar with Python's defaultdict, but could not find an equivalent working example. I have searched for an answer and hit those answers below but they do not solve my problem.
How to calculate a value for each key of a HashMap?
what java collection that provides multiple values for the same key
is there a Java equivalent of Python's defaultdict?
The desired output would be :
{1=105, 2=156, 3=183 , 4=204 ,5=206 ..... and so on}
Eventually the average per position(key) has to be calculated but that is a problem I think I can fix on my own when I know how to do the above.
EDIT : The real output is much much bigger ! Think about 100+ of the hashmaps with more than 100 keys.
Try with something like that
public Map<Integer, Integer> combine(List<Map<Integer, Integer>> maps) {
Map<Integer, Integer> result = new HashMap<Integer, Integer>();
for (Map<Integer, Integer> map : maps) {
for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
int newValue = entry.getValue();
Integer existingValue = result.get(entry.getKey());
if (existingValue != null) {
newValue = newValue + existingValue;
}
result.put(entry.getKey(), newValue);
}
}
return result;
}
Basically:
Create a new map for the result
Iterate over each map
Take each element and if already present in the result increment the value, if not put it in the map
return the result
newHashMap.put(key1,map1.get(key1)+map2.get(key1)+map3.get(key1));

What is the fastest method to find duplicates from a collection

This is what I have tried and somehow I get the feeling that this is not right or this is not the best performing application, so is there a better way to do the searching and fetching the duplicate values from a Map or as a matter of fact any collection. And a better way to traverse through a collection.
public class SearchDuplicates{
public static void main(String[] args) {
Map<Integer, String> directory=new HashMap<Integer, String>();
Map<Integer, String> repeatedEntries=new HashMap<Integer, String>();
// adding data
directory.put(1,"john");
directory.put(2,"michael");
directory.put(3,"mike");
directory.put(4,"anna");
directory.put(5,"julie");
directory.put(6,"simon");
directory.put(7,"tim");
directory.put(8,"ashley");
directory.put(9,"john");
directory.put(10,"michael");
directory.put(11,"mike");
directory.put(12,"anna");
directory.put(13,"julie");
directory.put(14,"simon");
directory.put(15,"tim");
directory.put(16,"ashley");
for(int i=1;i<=directory.size();i++) {
String result=directory.get(i);
for(int j=1;j<=directory.size();j++) {
if(j!=i && result==directory.get(j) &&j<i) {
repeatedEntries.put(j, result);
}
}
System.out.println(result);
}
for(Entry<Integer, String> entry : repeatedEntries.entrySet()) {
System.out.println("repeated "+entry.getValue());
}
}
}
Any help would be appreciated. Thanks in advance
You can use a Set to determine whether entries are duplicate. Also, repeatedEntries might as well be a Set, since the keys are meaningless:
Map<Integer, String> directory=new HashMap<Integer, String>();
Set<String> repeatedEntries=new HashSet<String>();
Set<String> seen = new HashSet<String>();
// ... initialize directory, then:
for(int j=1;j<=directory.size();j++){
String val = directory.get(j);
if (!seen.add(val)) {
// if add failed, then val was already seen
repeatedEntries.add(val);
}
}
At the cost of extra memory, this does the job in linear time (instead of quadratic time of your current algorithm).
EDIT: Here's a version of the loop that doesn't rely on the keys being consecutive integers starting at 1:
for (String val : directory.values()) {
if (!seen.add(val)) {
// if add failed, then val was already seen
repeatedEntries.add(val);
}
}
That will detect duplicate values for any Map, regardless of the keys.
You can use this to found word count
Map<String, Integer> repeatedEntries = new HashMap<String, Integer>();
for (String w : directory.values()) {
Integer n = repeatedEntries.get(w);
n = (n == null) ? 1 : ++n;
repeatedEntries.put(w, n);
}
and this to print the stats
for (Entry<String, Integer> e : repeatedEntries.entrySet()) {
System.out.println(e);
}
List, Vector have a method contains(Object o) which return Boolean value based either this object is exist in collection or not.
You can use Collection.frequency to find all possible duplicates in any collection using
Collections.frequency(list, "a")
Here is a proper example
Most generic method to find
Set<String> uniqueSet = new HashSet<String>(list);
for (String temp : uniqueSet) {
System.out.println(temp + ": " + Collections.frequency(list, temp));
}
References from above link itself

Using Java, how can I compare every entry in HashMap to every other entry in the same HashMap without duplicating comparisons?

I am currently using 2 for loops to compare all entries but I am getting duplicate comparisons. Because HashMaps aren't ordered, I can't figure out how to eliminate comparisons that have already been made. For example, I have something like:
for(Entry<String, String> e1: map.entrySet())
{
for(Entry<String, String> e2: map.entrySet())
{
if (e1.getKey() != e2.getKey())
{
//compare e1.getValue() to e2.getValue()
}
}
}
The problem with this is that the first entry will be compared to the second entry and then the third entry and so on. But then the second entry will again be compared to the first entry and so on. And then the third entry will be compared to the first entry, then the second entry, then the 4th entry, etc. Is there a better way to iterate through HashMaps to avoid doing duplicate comparisons?
Additional information:
To be more specific and hopefully answer your questions, the HashMap I have is storing file names (the keys) and file contents (the values) - just text files. The HashMap has been populated by traversing a directory that contains the files I will want to compare. Then what I am doing is running pairs of files through some algorithms to determine the similarity between each pair of files. I do not need to compare file 1 to file 2, and then file 2 to file 1 again, as I only need the 2 files to be compared once. But I do need every file to be compared to every other file once. I am brand new to working with HashMaps. agim’s answer below might just work for my purposes. But I will also try to wrap my brain around both Evgeniy Dorofeev and Peter Lawrey's solutions below. I hope this helps to explain things better.
If you are not careful, the cost of eliminating duplicates could higher than the cost of redundant comparisons for the keys at least.
You can order the keys using System.identityHashCode(x)
for(Map.Entry<Key, Value> entry1: map.entrySet()) {
Key key1 = entry1.getKey();
int hash1 = System.identityHashCode(key1);
Value value1 = entry1.getValue();
for(Map.Entry<Key, Value> entry2: map.entrySet()) {
Key key2 = entry2.getKey();
if (key1 > System.identityHashCode(key2)) continue;
Value value2 = entry1.getValue();
// compare value1 and value2;
}
}
How about this solution:
String[] values = map.values().toArray(new String[map.size()]);
for (int i = 0; i < values.length; i++) {
for (int j = i+1; j<values.length; j++) {
if (values[i].equals(values[j])) {
// ...
}
}
}
Try
HashMap<Object, Object> map = new HashMap<>();
Iterator<Entry<Object, Object>> i = map.entrySet().iterator();
while (i.hasNext()) {
Entry next = i.next();
i.remove();
for (Entry e : map.entrySet()) {
e.equals(next);
}
}
Note that there is no sense comparing keys in a HashMap they are always not equal. That is we could iterate / compare values only
If I understand correctly, you just want to know if there are any duplicates in the map's values? If so:
Set<String> values = new HashSet<String>(map.values());
boolean hasDuplicates = values.size() != map.size();
This could be made more efficient if you kick out once you find the first duplicate:
Set<String> values = new HashSet<String>();
for (String value : map.values()) {
if (!values.add(value)) {
return true;
}
}
return false;
public static boolean compareStringHashMaps(Map<String, String> expectedMap, Map<String, String> actualMap) throws Exception
{
logger.info("## CommonFunctions | compareStringHashMaps() ## ");
Iterator iteratorExpectedMap = expectedMap.entrySet().iterator();
Iterator iteratorActualMap = actualMap.entrySet().iterator();
boolean flag = true;
while (iteratorExpectedMap.hasNext() && iteratorActualMap.hasNext()){
Map.Entry expectedMapEntry = (Map.Entry) iteratorExpectedMap.next();
Map.Entry actualMapEntry = (Map.Entry) iteratorActualMap.next();
if(!expectedMapEntry.getKey().toString().trim().equals(actualMapEntry.getKey().toString().trim()))
{
flag = false;
break;
}
else if (!expectedMapEntry.getValue().toString().trim().equals(actualMapEntry.getValue().toString().trim()))
{
flag = false;
break;
}
}
return flag;
}
Considering the entries of a HashMap is Integer.
This returns the maximum entry within a HashMap.
int maxNum = 0;
for (Object a: hashMap.keySet()) {
if ((int)hashMap.get(a) > maxNum) {
maxNum = (int)hashMap.get(a);
}
}
You could try using a 2D array of results. If the result is already populated, then don't perform the comparison again. This also has the benefit of storing the results for later use.
So for an int result you would be looking at something like this: Integer[][] results = new Integer[map.entrySet().size()][map.entrySet().size()];This initialises the array to nulls and allows you to check for existing results before comparison. One important thing to note here is that each comparison result should be stored in the array twice, with the exception of comparisons to itself. e.g. comparison between index 1 and index 2 should be stored in results[1][2] and result[2][1].
Hope this helps.

Categories