So I have a hashmap which contains key as Strings and value as Integers of the count of those strings occurring in my Set
for eg I would have a hashMap as follows
Key Value
abcd 4 (meaning there are 4 duplicate strings of abcd in my Set defined someplace)
----- 13
b-b- 7
and so on..
Now what I am trying to do is remove all the empty strings entries from my HashMap. So in the above example I would want to remove all the empty strings with value 13. So my resulting HashMap would be
Key Value
abcd 4
b-b- 7
This is my code that tries to do the same. generateFeedbackMap() is function which returns the HashMap in consideration StringIterator is a class which I have defined which iterates over through each character of my Strings.
for(String key : generateFeedbackMap().keySet()) {
StringIterator it = new StringIterator(key);
int counter = 0;
while(it.hasNext()){
String nextChar = it.next();
if(nextChar.equals("-")){
counter++;
}
Iterator<Map.Entry<String, Integer>> mapIterator = generateFeedbackMap().entrySet().iterator();
if(counter >= key.length()){
while(mapIterator.hasNext()){
Map.Entry<String, Integer> entry = mapIterator.next();
if(entry.getKey().equals(key)){
mapIterator.remove();
}
}
}
}
}
So I increment the counter wherever I find a "-" character. When the counter equals my key string length which means it is an empty string, I remove it using Map Iterator but this does not remove the entry from my Map. What am I doing wrong?
generateFeedbackMap() makes it sound like you’re getting a copy of the underlying map, in which case removing a key from the copy won’t affect the underlying map. If you’re actually getting the map, then you should rename your method.
Regardless, the following would accomplish the same as your original code (but will only remove from the copy).
Map<String,Integer> feedbackMap = generateFeedbackMap();
for ( String key : feedbackMap.keySet() ) {
if ( key.matches("-+") ) {
feedbackMap.remove(key);
}
}
If you’re stuck getting a copy of the underlying map, then you do need to create your new helpfulMap. But you can still use a regular expression and other Map functions to speed things up:
Map<String,Integer> helpfulMap = new HashMap<>();
for ( Map.Entry<String,Integer> entry : generateFeedbackMap().entrySet() ) {
if ( ! entry.getKey().matches("-+") ) {
helpfulMap.put(entry.getKey(),entry.getValue());
}
}
Okay guys, I think I figured out a solution. I just copied all my current entries from oldMap to a new defined HashMap which would contain at least one letter in their keys. So essentially I got rid of all the removing and iterating over strings and just use another HashMap instead as below
Map<String, Integer> HelpfulMap = new HashMap<String,Integer>();
for(String key : generateFeedbackMap().keySet()) {
StringIterator it = new StringIterator(key);
while(it.hasNext()){
String nextChar = it.next();
if(!nextChar.equals("-")){
HelpfulMap.put(key, generateFeedbackMap().get(key));
}
}
}
I don't know what I was doing previously. I went for a good shower and came up with this idea and it worked. I love programming!
Thanks everyone for your inputs!
Related
I've got a HashMap which contains an ArrayList as value. I want to check if one of the lists contains an object and remove that object from the list. How can I achieve that?
I've tried using some for loops, but then I get a ConcurrentModificationException. I can't get that exception away.
My hashmap:
Map<String,List<UUID>> inAreaMap = new HashMap<String, ArrayList<UUID>>();
I intend to check if the ArrayList contains the UUID I've got, and if so, I want to remove it from that ArrayList. But I don't know the String at that position of the code.
What I already tried:
for (List<UUID> uuidlist : inAreaMap.values()) {
for (UUID uuid : uuidlist) {
if (uuid.equals(e.getPlayer().getUniqueId())) {
for (String row : inAreaMap.keySet()) {
if (inAreaMap.get(row).equals(uuidlist)) {
inAreaMap.get(row).remove(uuid);
}
}
}
}
}
There is a more elegant way to do this, using Java 8:
Map<String, ArrayList<UUID>> map = ...
UUID testId = ...
// defined elsewhere
// iterate through the set of elements in the map, produce a string and list for each
map.forEach((string, list) -> {
// as the name suggests, removes if the UUID equals the test UUID
list.removeIf(uuid -> uuid.equals(testId));
});
try with the iterator.
inareamap.iterator().. and.. iterator.remove()
If you have Java 8, the solution of camaron1024's solution is the best. Otherwise you can make use of the fact that you have a list and iterate through it backwards by index.
for(ArrayList<UUID> uuidlist : inareamap.values()) {
for(int i=uuidlist.size()-1;i>=0;i--) {
if (uuidlist.get(i).equals(e.getPlayer().getUniqueId()))
uuidlist.remove(i);
}
}
Here the easy solution.
UUID key = ... ;
for(Map.Entry<String,ArrayList<UUID>> e : hm.entrySet()){
Iterator<UUID> itr = e.getValue().iterator();
while(itr.hasNext()){
if(itr.next() == key)
itr.remove();
}
}
My program uses two HashmMap and they have exactly the same number of entries and the same keys.
One (tableMap) is static and never changes. The other one is dynamic (partitionMap), this means that I need to update values.
My algorithm got a problem, because seems to be adding one more entry when it is supposed to be not.
//I have a LinkedList of strings that I want to add to the HashMap partitionMap
LinkedList<String> partition = new LinkedList<String>();
for (TerminalNode terminalNode : ctx.U()) {
partition.add(terminalNode.getText());
}
//for each entry of tableMap
for(Entry<String, LinkedList<String>> entry : tableMap.entrySet())
{
//I retrieve keys and values from tableMap
String key = entry.getKey();
LinkedList<String> attributes = entry.getValue();
//the condition: if my linkedlist is included in the other do...
if(attributes.containsAll(partition))
{
//get the list of values
ArrayList<LinkedList<String>> l = partitionMap.get(key);
//but the first time is always null since I init partitionMap without values
if(l==null)
{
ArrayList<LinkedList<String>> firstLL = new ArrayList<LinkedList<String>>();
firstLL.add(partition);
partitionMap.put(key, firstLL); //BUG HERE! add one more entry instead of just updating values
}
else
{
l.add(partition);
partitionMap.put(key, l);
}
}
}
Does anybody have an idea why this is wrong?
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this question
Given a list/array of strings:
document
document (1)
document (2)
document (3)
mypdf (1)
mypdf
myspreadsheet (1)
myspreadsheet
myspreadsheet (2)
How do I remove all the duplicates but retain only the highest copy number?
Ending result to be:
document (3)
mypdf (1)
myspreadsheet (2)
You put in a broad question, so here comes an unspecific (but nonetheless) "complete" answer:
Iterate over all your strings to identify all lines that contain braces.
In other words: identify all the strings that look like "X (n)"
Then, for each "different" X that you found, you can iterate the list again; so that you can find all occurrences of "X", X (1)", .. and so on
Doing so will allow you to detect the maximum n for each of your Xes.
Push that "maximum" "X (n)" into your results list.
In other words: it only takes such a simple receipt to solve this problem; now it only takes your time to turn these pseudo-code instructions into real code.
For the record: if the layout of your file is really as shown above, then things become a bit easier - as it seems that your numbers are just increasing. What I mean is:
X (1)
X (2)
X (3)
is easier to treat than
X (1)
X (3)
X (2)
As in your case, it seems save to assume that the last X(n) contains the largest n. Which makes using a HashMap (as suggested by cainiaofei) a nice solution.
an alternative solution
use a HashMap the key is the name (e.g. the name of document document (1)
document (2) document (3) are all document)
which can be implement by this code str.substring(0,str.indexOf('(')).trim()
and the value is the times that key present, at last traverse the map get the key that corresponding value is max and the result is key(value-1)
I would advice you to use a dictionnary :
Map<String, Integer> dict = new HashMap<>();
for (String s : listOfInput){
String name = s.split(" ")[0];
String version = s.split(" ")[1].charAt(1);
if(dict.get(name)!=null){
if (Integer.parseInt(version) < dict.get(name)){
continue;
}
}
dict.put(name, version);
}
The data would be at the end in the dictionary:
key | value
document | 3
mypdf | 1
myspreadsheet | 2
This is a simple solution by making use of a Map. First you loop through your list, you split the String, and you add it to the map with the name as the key, and what's inside the paranthesis as a value. And for each entry you check if the key already exists. And if the key exists you compare the value and you add the next entry to the map if the value is bigger than what's already stored. At the end you loop through the map and get your list.
This should probably work with any kind of input. I think...
Of course this can be done better than this. If anybody has suggestions, please feel free to share them.
public static void main(String[] args) {
List<String> list = Arrays.asList("document", "document (1)", "document (2)", "document (3)", "mypdf (1)", "mypdf", "myspreadsheet (1)",
"myspreadsheet", "myspreadsheet (2)");
Map<String, Integer> counterMap = new HashMap<>();
List<String> newList = new ArrayList<>();
for (String item : list) {
if (item.indexOf(')') != -1) {
String namePart = item.substring(0, item.indexOf('(')).trim();
Integer numberPart = Integer.parseInt(item.substring(item.indexOf('(') + 1, item.indexOf(')')));
Integer existingValue = counterMap.get(namePart);
if (existingValue != null) {
if (numberPart > existingValue) {
counterMap.put(namePart, numberPart);
}
} else {
counterMap.put(namePart, numberPart);
}
} else {
newList.add(item);
}
}
Iterator<Entry<String, Integer>> iterator = counterMap.entrySet().iterator();
while (iterator.hasNext()) {
Entry<String, Integer> next = iterator.next();
String key = next.getKey();
Integer value = next.getValue();
if (newList.contains(key)) {
newList.remove(key);
}
newList.add(key + " (" + value + ")");
}
System.out.println(newList);
}
Here is a possible approach, but this will only work if the version number doesn't exceed 9 (*) :
1) Sort the list in reverse order, so that the most recent version appears first
(*) The sorting being based on alphabetical order , you should be quite fine unless your version number exceeds one digit.Because 10 for instance, appears before 9 with an alphabetical sorting.
Your list will turn into :
myspreadsheet (2)
myspreadsheet (1)
myspreadsheet
mypdf (1)
mypdf
document (3)
document (2)
document (1)
document
2) Iterate on the list, and only keep the first occurence of a given document (i.e the most recent thanks to the reverse sorting)
3) If you want to, sort back the remaining list to a more natural ordering
List<String> documents = new ArrayList<String>();
documents.add("document");
documents.add("document (1)");
documents.add("document (2)");
documents.add("document (3)");
documents.add("mypdf (1)");
documents.add("mypdf");
documents.add("myspreadsheet (1)");
documents.add("myspreadsheet");
documents.add("myspreadsheet (2)");
// 1) Sort in reverse order, so that the most recent document version appears first
Collections.sort(documents, Collections.reverseOrder());
String lastDocumentName = "";
ListIterator<String> iter = documents.listIterator();
// 2)
while (iter.hasNext()) {
String document = iter.next();
// Store the first part of the String , i.e the document name (without version)
String firstPart = document.split("\\s+")[0];
// Check if this document is a version of the last checked document
// If it is the case, this version is anterior, remove it from the list
if (lastDocumentName.equals(firstPart)) {
iter.remove();
}
// Store this document's name as the last one checked
lastDocumentName = firstPart;
}
// 3) Sort back to natural order
Collections.sort(documents);
for (String doc : documents) {
System.out.println(doc);
}
Let's utilize the Stream API to group our documents and simply pick the newest revision by sorting Strings by the revision number. Keep in mind that those static methods were implemented poorly because you did not give us too much information about the naming strategy but the idea should be clear.
Algorithm:
Group revisions of the same String together
Pick the number with the highest version from each group
Solution:
Map<String, List<String>> grouped = input.stream()
.collect(Collectors.groupingBy(preprocessedString(), Collectors.toList()));
List<String> finalResult = grouped.entrySet().stream()
.map(e -> e.getValue().stream()
.max(Comparator.comparing(revisionNumber())).get()) //at this point we have at least one element
.collect(Collectors.toList());
}
Helper parsing functions:
private static Function<String, Integer> revisionNumber() {
return s -> s.contains("(") ? Integer.valueOf(s.substring(s.indexOf('(') + 1, s.indexOf(')'))) : 0;
}
private static Function<String, String> preprocessedString() {
return s -> s.contains("(") ? s.substring(0, s.lastIndexOf("(")).trim() : s.trim();
}
Input:
List<String> input = Arrays.asList(
"document",
"document (1)",
"document (2)",
"document (3)",
"mypdf (1)",
"mypdf",
"myspreadsheet (12)",
"myspreadsheet",
"myspreadsheet (2)",
"single");
Result:
[single, myspreadsheet (12), document (3), mypdf (1)]
We do not need actually to know if the element contains more then one whitespace or whatever. We can aways start from the end and check if the elements is duplicate or not (see if there is a ")" or not).
Also interating once through the List is enough to get all the information we need. Assuming that, I am providing a solution which saves the highest appearance value as a VALUE in a Map which map will have as KEYs all elements in the given input list.
After that you can create your result List with one more iteration through the Map.
public List<String> removeDuplicates(List<String> inputArray) {
Map<String, Integer> map = new HashMap<String, Integer>();
List<String> result = new ArrayList<String>();
int numberOfOcurences = 0;
for (int i = 0; i < inputArray.size(); i++) {
String element = inputArray.get(i);
if (element.charAt(element.length() - 1) == ')') {
numberOfOcurences = Character.getNumericValue(element.charAt(element.length() - 2));
element = element.substring(0, element.length() - 4);
} else {
numberOfOcurences = 0;
}
if (map.isEmpty()) {
map.put(element, numberOfOcurences);
} else {
if (null != map.get(element) && map.get(element) < numberOfOcurences) {
map.put(element, numberOfOcurences);
} else if (null == map.get(element)) {
map.put(element, numberOfOcurences);
}
}
}
for (String a : map.keySet()) {
result.add(a + " (" + map.get(a)+ ")");
}
return result;
}
Set<T> mySet = new HashSet<T>(Arrays.asList(Your));
I have found that from another user of stackoverflow, try if it works. Good Luck :)
I'm using several techniques here, so it's hard to find help online.
I need to populate a HashMap<String, String> with values I take from part of a StringBuilder, then take the keys and add them into an ArrayList<String>, then print the list. But when I print, I get a list full of nulls. I don't know why, I thought it would print the values I got from the StringBuilder. It should print: Values taken from the hashmap keys: ABCDEFGHI. (The reason I used StringBuilder is because String is immutable, is this correct thinking too?)
Also, I figured using a loop to print the list is okay, since my keys are actually numbers.
I've never created a HashMap before, so maybe I'm missing something. Thanks for your help.
Here is my code:
// create HashMap from String
StringBuilder alphaNum = new StringBuilder("1A2B3C4D5E6F7G8H9I");
Map<String, String> myAlphaNum = new HashMap<String, String>(9);
// for loop puts key and values in map, taken from String
for (int i = 0; i < alphaNum.length();)
{
myAlphaNum.put(alphaNum.substring(i, ++i), alphaNum.substring(i, ++i));
}
if (myAlphaNum.containsKey(1))
System.out.println("Key is there.");
else
System.out.println("Key is null.");
// create ArrayList, add values to it using map keys
ArrayList<String> arrayList = new ArrayList<String>();
// for loop gets the "number" keys from HashMap to get the "letter" values
for (int j = 1; j <= myAlphaNum.size(); j++)
arrayList.add(myAlphaNum.get(j));
System.out.print("Values taken from the hashmap keys: ");
for (String list : arrayList)
System.out.print(list);
Console:
Key is null.
Values taken from the hashmap keys: nullnullnullnullnullnullnullnullnull
You are using containsKey/get with an Integer as parameter, while your map keys are defined as String. That's why you got null.
I would recommend to use a Map<Integer, String> myAlphaNum = new HashMap<Integer, String>(9); and in your loop myAlphaNum.put(Integer.parseInt(alphaNum.substring(i, ++i)), alphaNum.substring(i, ++i));. Then you'll get your desired output.
Also you could the ArrayList constructor that takes a Collection as parameter (or just sysout myAlphaNum.values()) directly.
// create ArrayList, add values to it using map keys
ArrayList<String> arrayList = new ArrayList<String>(myAlphaNum.values());
System.out.println(arrayList); //[A, B, C, D, E, F, G, H, I]
myAlphaNum has keys of type String, so passing an int to get (myAlphaNum.get(j)) will always return null.
There are several ways to iterate over the values (or keys or entries) of the map.
For example (assuming you only care about the values) :
for (String value : myAlphaNum.values())
arrayList.add(value);
// create HashMap from String
StringBuilder alphaNum = new StringBuilder("1A2B3C4D5E6F7G8H9I");
Map<String, String> myAlphaNum = new HashMap<String, String>(9);
// for loop puts key and values in map, taken from String
for (int i = 0; i < alphaNum.length();)
{
myAlphaNum.put(alphaNum.substring(i, ++i), alphaNum.substring(i, ++i));
}
System.out.println(myAlphaNum);
if (myAlphaNum.containsKey(1))
System.out.println("Key is there.");
else
System.out.println("Key is null.");
// create ArrayList, add values to it using map keys
ArrayList<String> arrayList = new ArrayList<String>();
// for loop gets the "number" keys from HashMap to get the "letter" values
for (int j = 1; j <= myAlphaNum.size(); j++)
arrayList.add(myAlphaNum.get(j+""));
System.out.print("Values taken from the hashmap keys: ");
for (String list : arrayList)
System.out.print(list);
You can try the above code. You have used string key bit while retriving Integer so it wont return anything.
I found this program in my text book, which basically counts the occurence of each string in the String array tst.
public class Test {
private static HashMap<String, Integer> mp = new HashMap<String, Integer>();
public static void main(String[] args) {
String[] tst = new String[] { "ABC", "DEF", "DEF", "DEF","ABC", "DEF", "ABC" };
checkMap(tst);
}
public static void checkMap(String[] str) {
for (String st : str) {
if (!mp.containsKey(st)) {
mp.put(st, 1);
}
else {
Integer ct = mp.get(st);
if(ct!=null)
{
ct++;
mp.put(st, ct);
}
}
}
for (Map.Entry<String, Integer> entry : mp.entrySet()) {
System.out.println(entry.getKey() + " ocurrs " + entry.getValue()+ " times");
}
}
}
The output for the code is -
ABC ocurrs 3 times
DEF ocurrs 4 times
My question is in the if/else statement here -
if (!mp.containsKey(st)) {
mp.put(st, 1);
}
else {
Integer ct = mp.get(st);
if(ct!=null)
{
ct++;
mp.put(st, ct);
}
}
When we haven't put any entries inside the hashmap (the hashmap is empty), on what basis does this work? Apologies if this is a very basic question, but I found no answer anywhere online that explains this. I am confused with what is written in the if/else loop.
Also, this line here -
Integer ct = mp.get(st);
How can we get the value to which the key is mapped when infact the hashmap is actually empty? I am trying to relate this to an array - If you query elements of an array once its created, but not initialized, it throws a null pointer. Someone, please explain how this works for a hashmap. Once again, apologies for asking such a basic question.
Well, in this line you check whether the map contains a key
if (!mp.containsKey(st)) {
Since there is a ! before the expression, this means "if the map does not contain a key". After that, "then" block follows where you insert a key in the map with value 1 (since it does not exist).
Otherwise if the key does exist (the else block), you take the value for that key, increment it (ct++) and add it again to the map for the same key.
Let me just say that the null check (if(ct!=null)) is not necessary for this code.
General remark on this question:
How can we get the value to which the key is mapped when infact the hashmap is actually empty?
If you try to get something from the HashMap for a key that is not present in the map, the map returns null. That is true for any key you try to get from an empty map.
Can you please explain what this means though - Integer ct = mp.get(st);
map.get(key) returns a value that is stored for that key. The map itself is a collection of key-value pairs, which means: for each key there is one value in the map. So to get the value stored for that key you invoke map.get(key). If you store map.put("ABC", 10) the map will return 10 for map.get("ABC").
This is because of containsKey function checks if the hashMap contains particular key.
If the HashMap is mpty and you try to do a get on non existant key you will get a null value
st is get here by the for (String st : str) loop. It has nothing to do with the HashMap.
if (!mp.containsKey(st)) {
This tests if the HashMap does not contain the key st. If there are no items it obviously can not contain the key. Then in the else block it uses mp.get(st), which will now always succeed because it has been checked that mp contains st (actually, it does not not contain it).
The null check if (ct == null) is here because if for some reason the map contained null for the key in question. That however shouldn't be possible if the code only puts integers to the map and tests for the existence of the key, so the null check coulb be removed.
The test:
if (!mp.containsKey(st))
tests if there is no entry in the map by that key.
It is therefore logical that in the else branch, the entry exists and has a non null value... Which makes the ct == null test redundant.
And when the value exists, the code get()s the existing value, adds 1 to it (in fact it creates a new Integer but that's another story) and put()s back the new value.
Note that that code mixes autoboxing and non autoboxing. mp.put(st, 1) does autoboxing; behind the scenes it really does mp.put(st, new Integer(1)).
Similarly:
Integer ct = mp.get(st);
ct++;
is really:
Integer ct = mp.get(st);
Integer tmp = new Integer(ct.intValue() + 1);
ct = tmp;
The null check is not necessary. Either the key is contained in the map and its value is not null, or it is not contained in the map.
The reason we can be confident about the value never being null is that the map (and all its contents) is defined and used in the method, and there's no opportunity for a null to get it there.
Although the get() method will return null if passed a key that it doesn't contain, that will never happen with this code.
Anyway, the code is inelegant: All those lines can be expressed as one simple line:
mp.put(mp.containsKey(st) ? mp.get(st) + 1 : 1);