Remove entry from Map if object meets certain criteria - java

I have a map of objects, and I want to delete it from the map if the objects attributes meet a certain criteria.
The map is as follows
Map<String, ExchangeSummaryItem> under20 = mapper.readValue(new URL("https://rsbuddy.com/exchange/summary.json"), new TypeReference<Map<String, ExchangeSummaryItem>>() {});
Each ExchangeSummary has a sell_average, sell_quantity, and buy_quantity, and I want to remove it from the map if sell_average > 2000, and buy/sell quantities are both 0.
My current code is as follows but doesn't successful remove any values from the map (the map still had the same size)
for (ExchangeSummaryItem item : under20.values()) {
int ObjSellAverage = item.getSellAverage();
int ObjSellQ = item.getSellQuantity();
int ObjBuyQ = item.getBuyQuantity();
if (ObjSellAverage > 20000 && ObjSellQ == 0 && ObjBuyQ == 0){
System.out.println(under20.size());
under20.remove(item);
}
}
Any help as to why this is happening would be much appreciated! Thank-you!

under20.remove(item); is calling remove with the value. It expects the key.
You also can't just change to iterating over under20.keySet() and calling remove, because you'd have a ConcurrentModificationException.
One easy way to solve it is to create another map:
Map<String, ExchangeSummaryItem> result = new HashMap<>();
//Map.entrySet() gives you access to both key and value.
for (Map.Entry<String,ExchangeSummaryItem> item : under20.entrySet()) {
int ObjSellAverage = item.getValue().getSellAverage();
int ObjSellQ = item.getValue().getSellQuantity();
int ObjBuyQ = item.getValue().getBuyQuantity();
if (!(ObjSellAverage > 20000 && ObjSellQ == 0 && ObjBuyQ == 0)){
result.put(item.getKey(), item.getValue());
}
}
And use filtered values in result

Related

How can I check for duplicate values of an object in an array of objects, merge the duplicates' values, and then remove the duplicate?

Right now I have an array of "Dragon"s. Each item has two values. An ID and a Count. So my array would look something like this:
Dragon[] dragons = { new Dragon(2, 4),
new Dragon(83, 199),
new Dragon(492, 239),
new Dragon(2, 93),
new Dragon(24, 5)
};
As you can see, I have two Dragons with the ID of 2 in the array. What I would like to accomplish is, when a duplicate is found, just add the count of the duplicate to the count of the first one, and then remove the duplicate Dragon.
I've done this sort of successfully, but I would end up with a null in the middle of the array, and I don't know how to remove the null and then shuffle them.
This is what I have so far but it really doesn't work properly:
public static void dupeCheck(Dragon[] dragons) {
int end = dragons.length;
for (int i = 0; i < end; i++) {
for (int j = i + 1; j < end; j++) {
if (dragons[i] != null && dragons[j] != null) {
if (dragons[i].getId() == dragons[j].getId()) {
dragons[i] = new Item(dragons[i].getId(), dragons[i].getCount() + dragons[j].getCount());
dragons[j] = null;
end--;
j--;
}
}
}
}
}
You should most probably not maintain the dragon count for each dragon in the dragon class itself.
That aside, even if you are forced to use an array, you should create an intermeditate map to store your dragons.
Map<Integer, Dragon> idToDragon = new HashMap<>();
for (Dragon d : yourArray) {
// fetch existing dragon with that id or create one if none present
Dragon t = idToDragon.computeIfAbsent(d.getId(), i -> new Dragon(i, 0));
// add counts
t.setCount(t.getCount() + d.getCount());
// store in map
idToDragon.put(d.getId(), t);
}
Now the map contains a mapping between the dragons' ids and the dragons, with the correct counts.
To create an array out of this map, you can just
Dragon[] newArray = idToDragon.values().toArray(new Dragon[idToDragon.size()]);
You may be force to store the result in an array but that doesn't mean that you're force to always use an array
One solution could be using the Stream API, group the items adding the count and save the result into an array again. You can get an example of how to use the Stream API to sum values here. Converting a List<T> into a T[] is quite straightforward but anyways, you have an example here
The size of an array cannot be changed after it's created.
So you need to return either a new array or list containing the merged dragons.
public static Dragon[] merge(Dragon[] dragonArr) {
return Arrays.stream(dragonArr)
// 1. obtain a map of dragon IDs and their combined counts
.collect(groupingBy(Dragon::getId, summingInt(Dragon::getCount)))
// 2. transform the map entries to dragons
.entrySet().stream().map(entry -> new Dragon(entry.getKey(), entry.getValue()))
// 3. collect the result as an array
.toArray(Dragon[]::new);
}

Optimisation of searching HashMap with list of values

I have a map in which values have references to lists of objects.
//key1.getElements() - produces the following
[Element N330955311 ({}), Element N330955300 ({}), Element N3638066598 ({})]
I would like to search the list of every key and find the occurrence of a given element (>= 2).
Currently my approach to this is every slow, I have a lot of data and I know execution time is relative but it takes 40seconds~.
My approach..
public String occurance>=2 (String id)
//Search for id
//Outer loop through Map
//get first map value and return elements
//inner loop iterating through key.getElements()
//if match with id..then iterate count
//return Strings with count == 2 else return null
The reason why this is so slow is because I have a lot of ids which I'm searching for - 8000~ and I have 3000~ keys in my map. So its > 8000*3000*8000 (given that every id/element exists in the key/valueSet map at least once)
Please help me with a more efficient way to make this search. I'm not too deep into practicing Java, so perhaps there's something obvious I'm missing.
Edited in real code after request:
public void findAdjacents() {
for (int i = 0; i < nodeList.size(); i++) {
count = 0;
inter = null;
container = findIntersections(nodeList.get(i));
if (container != null) {
intersections.add(container);
}
}
}
public String findIntersections(String id) {
Set<Map.Entry<String, Element>> entrySet = wayList.entrySet();
for (Map.Entry entry : entrySet) {
w1 = (Way) wayList.get(entry.getKey());
for (Node n : w1.getNodes()) {
container2 = String.valueOf(n);
if (container2.contains(id)) {
count++;
}
if (count == 2) {
inter = id;
count = 0;
}
}
}
if (inter != (null))
return inter;
else
return null;
}
Based on the pseudocode provided by you, there is no need to iterate all the keys in the Map. You can directly do a get(id) on the map. If the Map has it, you will get the list of elements on which you can iterate and get the element if its count is > 2. If the id is not there then null will be returned. So in that case you can optimize your code a bit.
Thanks

Getting intersection of a keys of a map

This is my map
Map<String,Set<Integer>> transactions = new HashMap<String,Set<Integer>>();
This is the Set of string
Set<String> check_set
Now question is how to make this method
Set<Integer> getIntersection(Map<String,Set<Integer>> transactions, Set<String> check_set)
Which will return intersection of all the Set of integers corresponding to the keys in check_set.
If the MAP is
tea=[1,3,5,7,9],
milk=[2,3,6,7,9],
sugar=[1,4,6,8,9]...
and if check_set is {"tea","milk"} then the method should return values which are present in both tea and milk i.e {3,7,9}. Or if it has {"milk","sugar"} then it should return {6,9}. if it has all three {"sugar","milk",tea"} then it should return {3,9}.
I understand I need to use retainAll function to get intersection. But how to formulate a logic to check all sets of integer in map and get their intersection.
EDIT : must use a copy of the first set in map (thanks to Bohemian)
In pseudo-code, you could have
Init result set to null to know it is still not initialized
Loop for each key in check_set
if result is null then result = map{key} // take first set
else result = intersection(result, map{key}) // and keep on intersecting
That gives in java
Set<Integer> result = null;
for (String key: check_set) {
if (result == null) {
result = new HashSet<Integer>(map.get(key));
}
else {
result.retainAll(map.get(key));
}
}
private static Set<Integer> getIntersection(Map<String, Set<Integer>> transactions, Set<String> checkSet) {
Iterator<String> iterator = checkSet.iterator();
// create a copy of the original set
Set<Integer> result = new HashSet<>(transactions.get(iterator.next()));
while (iterator.hasNext()) {
result.retainAll(transactions.get(iterator.next()));
}
return result;
}

Find next smallest key (performance advice needed)

I need some advice in terms of performance. I've got a Map<DateTime, String>. And I need something like the following method:
Map<DateTime, BigDecimal> map; // about 50 entries. Btw: Which impl to choose?
BigDecimal findNextSmaller(DateTime input) {
DateTime tmp = null;
for(DateTime d : map.keySet()) {
if(tmp == null && d < input) {
tmp = d;
}
if(d < input && d > tmp) {
tmp = d;
}
}
return map.get(tmp);
}
So basically I just iterate over the keySet of my Map and try to find the key which is the next smallest compared to input.
This method will get called about 1.000.000 times in a row:
BigDecimal sum;
List<Item> items; // about 1.000.000 Items
for(Item i : items) {
sum = sum.add(findNextSmaller(i.getDateTime()));
}
Now I'm looking for a way to make things faster.
My first thought was to make an OrderedList out of the Map's keySet. So in average I just have to iterate over half of the DateTimes. And then just do a map.get(dateTimeFromOrderedList) to get the matching value.
But is that all I can do about it?
You can use a TreeMap which has a built-in method for that:
TreeMap<DateTime, BigDecimal> map = new TreeMap<>();
//populate the map
BigDecimal findNextSmaller(DateTime input) {
return map.ceilingEntry(input).getValue(); //add exception checking as required
}
Note: you may want ceilingEntry or higherEntry depending on whether you want (resp.) >= or >.
Have a look at NavigableMap. This seems to be exactly what you need.
As you are searching for the DateTime closest and strictly less than the input, I would choose floorEntry(key) for the lookup. But make sure that you are handling nulls correctly. There may not be a key in the map that is strictly smaller than the input! If you try to add a null reference to a BigDecimal, a NullPointerException will be thrown.

How to update a value for a key in HashMap?

I am having HashMap like this,
HashMap<String,Set<String>> map = new HashMap<String,Set<String>>();
I am trying to do before adding an element in map,
Want to check whether the key exist or not, i can get it by using map.containsKey().
If the key exist, i want check the size of Set respective to that key.
If size <= 1 i want add an element in that set.
I wouldn't use containsKey and get as this means two lookups when you only need one.
private final Map<String,Set<String>> map = new HashMap<String,Set<String>>();
Set<String> set = map.get(key);
if(set != null && set.size() <= 1)
set.add(some$value);
The only problem with this is that the value will always be null unless you set it somewhere so what you may want is
private final Map<String,Set<String>> map = new HashMap<String,Set<String>>();
Set<String> set = map.get(key);
if(value != null)
map.put(key, set = new HashSet<String>());
if (set.size() <= 1)
set.add(some$value);
It is unusual to have a set with a maximum size of 2. Is there any reason for this?
Sounds like this:
HashMap<String,Set<String>> map = new HashMap<String,Set<String>>();
Set<String> value = map.get("key");
if(value != null) {
if(value.size() <= 1) {
value.add("some value");
}
} else {
map.put("key", new HashSet<String>());
}
Now, either the last point was poorly worded (i.e. you want to update the Set associated with the key) or you really want to update the key itself, in which case you'd probably have to just remove it and add a new entry.
You could get the set from the map with map.get(String key).
Then test the size of the Set. If needed, add your element.
Now you can simply remove the old set from the map with map.remove(String key) and reinsert it with put(String, Set);

Categories