I have a HashMap made of two types: String as Keys, which are Player names and Integer as values, which are scores.
I am creating a ranking system, and for that, I need to get the two highest values from this HashMap, so the top two players that were put into it.
For example:
If these were the values of my HashMap:
Key Value
String1 1
String2 2
String3 3
String4 4
String5 5
I would want a way to only return String4 and String5.
I thought that getting the entrySet would be enough, but there's no get method in a Set, so I can't get value 0 (highest) and value 1 (second highest).
I also tried using
Collections.max(map);
And it wouldn't accept a HashMap as an argument
And
final String[] top = new String[1];
int topInt = 0;
map.forEach((s, i) -> {if (i > topInt) top[0] = s;});
But that didn't quite work either, it was way too slow for my performance.
How do I obtain both highest values?
Try something like this. I haven't tested it, let me know if I overlooked something.
int highest = Integer.MIN_VALUE;
String highestString = null;
int secondHighest = Integer.MIN_VALUE;
String secondHighestString = null;
for(Map.Entry<String, Integer> pair : yourHashMap.entrySet())
{
if(highest < pair.getValue())
{
secondHighest = highest;
secondHighestString = highestString;
highest = pair.getValue();
highestString = pair.getKey();
}
}
Also as others have stated in the comments; this is probably not the best approach, as you could have multiple values of the same value with different keys. If indexing is what you are after use an array, ArrayList, or something from the java.util.Collections class.
Related
I am trying to compare elements in Array. for example,
labels = ["abc1","abc2","abc3","abc4"]
I want to take the String with the highest values. In this case its abc4. I'm pretty new to coding so if anyone could help me with the logic, it would be great.
What you are looking for is a method that compares each string to each other.
Java has a built-in method way to do this for the String class, called the compareTo method. As it's name suggests, it compares one string to another.
String a = "abc1";
String b = "abc2";
String c = "abc1";
System.out.println(a.compareTo(b)); // prints a negative number because `a` is smaller than `b`
System.out.println(b.compareTo(a)); // prints a positive number because `b` is bigger than `a`
System.out.println(c.compareTo(a)); // prints 0 because `a` and `b` have the same letters.
See the official java doc for the compareTo method:
[Returns]the value 0 if the argument string is equal to this string; a value less than 0 if this string is lexicographically less than the string argument; and a value greater than 0 if this string is lexicographically greater than the string argument.
The way you could use this in your example would be:
String biggest = labels[0];
for(int i = 1; i < labels.length; i++){
if(biggest.compareTo(labels[i]) < 0) biggest = labels[i];
}
System.out.println(biggest);
Note: For more details on how this method chooses which one is "bigger", see the java doc (linked above). If you have your own rules about which one should be bigger, then you can make your own method to define that.
UPDATE:
For example, see XtremeBaumer's comment
"abc20".compareTo("abc100") = 1. Indicating that abc20 is bigger than abc100, thus making compareTo() not necessarily useful for the task
Your question need improvement, based on what you said, lets remove all the abc from the Strings ,get the max integer and then return or print "abc" concatenated to the max number :
import java.util.Arrays;
import java.util.stream.IntStream;
public class Solution {
public static void main(String[] args) throws Throwable {
int numberOfElements = 100;
String[] labels = new String[numberOfElements];
Arrays.setAll(labels, element -> "abc" + element);
int max = Arrays.stream(labels).mapToInt(element -> Integer.parseInt(element.substring(3))).max().getAsInt();
System.out.println(String.join(" | ", labels));
System.out.println();
System.out.println();
System.out.println("The max here is : ");
System.out.println("abc" + max);
}
}
Output here :
abc0 | | abc1 | | abc2 | | abc3 | | abc4 ...... || abc99
The max here is :
abc99
Try something like this:
var strings = new ArrayList<String>();
strings.add("abc1");
strings.add("abc2");
strings.add("abc3");
strings.add("abc4");
var integers = strings
.stream()
.map(string -> Integer.valueOf(string.replaceAll("[^0-9]+", "")))
.collect(Collectors.toList());
var max = Collections.max(integers);
var indexMax = integers.indexOf(max);
var maxString = strings.get(indexMax);
System.out.println(maxString);
Simpler way : #mcieciel has already posted this one.
List<String> list = Arrays.asList("abc1", "abc22", "abc33", "abc19");
List<Integer> intList = list.stream()
.map(s -> Integer.parseInt(s.replaceAll("[^0-9]+", "")))
.collect(Collectors.toList());
int index = intList.indexOf(Collections.max(intList));
System.out.println(list.get(index));
Another way is creating a map which will have string and its corresponding integer value in key-value pairs.Then find the max value with its key from the map.
This might be an overkill .
String key = list.stream()
.collect(Collectors.toMap( // creating map like this format : abc1->1 , abc22->22 ...
Function.identity(), // key of the map i.e the string value
s -> Integer.parseInt(s.replaceAll("[^0-9]+", "")), // integer value
(e1, e2) -> e1)) // if multiple same entry exists choose one
.entrySet() // now we have map, we can iterate and find out the key which holds max value
.stream()
.max((e1, e2) -> Integer.compare(e1.getValue(), e2.getValue())) // comparing values
.get()
.getKey();
System.out.println(key);
Note : if you have string values without any digit,both will not work.
So I'm building a TreeMap from scratch and I'm trying to count the number of occurrences of every word in a text using Java. The text is read from a text file, but I can easily read it from there. I really don't know how to count every word, can someone help?
Imagine the text is something like:
Over time, computer engineers take advantage of each other's work and invent algorithms for new things. Algorithms combine with other algorithms to utilize the results of other algorithms, in turn producing results for even more algorithms.
Output:
Over 1
time 1
computer 1
algotitms 5
...
If possible I want to ignore if it's upper or lower case, I want to count them both together.
EDIT: I don't want to use any sort of Map (hashMap i.e.) or something similiar to do this.
Break down the problem as follows (this is one potential solution - not THE solution):
Split the text into words (create list or array or words).
Remove punctuation marks.
Create your map to collect results.
Iterate over your list of words and add "1" to the value of each encountered key
Display results (Iterate over the map's EntrySet)
Split the text into words
My preference is to split words by using space as a delimiter. The reason being is that, if you split using non-word characters, you may missed on some hyphenated words. I know that the use of hyphenation is being reduced, there are still plenty of words that fall under this rule; for example, middle-aged. If a word such as this is encountered, it MIGHT have to be treated as one word and not two.
Remove punctuation marks
Because of the decision above, you will need to first remove punctuation characters that might attached to your words. Keep in mind that if you use a regular expression to split the words, you might be able to accomplish this step at the same time you are doing the step above. In fact, that would be preferred so that you don't have to iterate over twice. Do both of these in a single pass. While you at it, call toLowerCase() on the input string to eliminate the ambiguity between capitalized words and lowercase words.
Create your map to collect results
This is where you are going to collect your count. Using the TreeMap implementation of the Java Map. One thing to be aware about this particular implementation is that the map is sorted according to the natural ordering of its keys. In this case, since the keys are the words from the inputted text, the keys will be arranged in alphabetical order, not by the magnitude of the count. IF sorting the entries by count is important, there is a technique where you can "reverse" the map and make the values the keys and the keys to values. However, since two or more words could have the same count, you will need to create a new map of <Integer, Set>, so that you can group together words with the same count.
Iterate over your list of words
At this point, you should have a list of words and a map structure to collect the count. Using a lambda expression, you should be able to perform a count() or your words very easily. But, if you are not familiarized or comfortable with Lambda expressions, you can use a regular looping structure to iterate over your list, do a containsKey() check to see if the word was encountered before, get() the value if the map already contains the word, and then add "1" to the previous value. Lastly, put() the new count in the map.
Display results
Again, you can use a Lambda Expression to print out the EntrySet key value pairs or simply iterate over the entry set to display the results.
Based on all of the above points, a potential solution should look like this (not using Lambda for the OPs sake)
public static void main(String[] args) {
String text = "Over time, computer engineers take advantage of each other's work and invent algorithms for new things. Algorithms combine with other algorithms to utilize the results of other algorithms, in turn producing results for even more algorithms.";
text = text.replaceAll("\\p{P}", ""); // replace all punctuations
text = text.toLowerCase(); // turn all words into lowercase
String[] wordArr = text.split(" "); // create list of words
Map<String, Integer> wordCount = new TreeMap<>();
// Collect the word count
for (String word : wordArr) {
if(!wordCount.containsKey(word)){
wordCount.put(word, 1);
} else {
int count = wordCount.get(word);
wordCount.put(word, count + 1);
}
}
Iterator<Entry<String, Integer>> iter = wordCount.entrySet().iterator();
System.out.println("Output: ");
while(iter.hasNext()) {
Entry<String, Integer> entry = iter.next();
System.out.println(entry.getKey() + ": " + entry.getValue());
}
}
This produces the following output
Output:
advantage: 1
algorithms: 5
and: 1
combine: 1
computer: 1
each: 1
engineers: 1
even: 1
for: 2
in: 1
invent: 1
more: 1
new: 1
of: 2
other: 2
others: 1
over: 1
producing: 1
results: 2
take: 1
the: 1
things: 1
time: 1
to: 1
turn: 1
utilize: 1
with: 1
work: 1
Why did I break down the problem like this for such mundane task? Simple. I believe each of those discrete steps should be extracted into functions to improve code reusability. Yes, it is cool to use a Lambda expression to do everything at once and make your code look much simplified. But what if you need to some intermediate step over and over? Most of the time, code is duplicated to accomplish this. In reality, often a better solution is to break these tasks into methods. Some of these tasks, like transforming the input text, can be done in a single method since that activity seems to be related in nature. (There is such a thing as a method doing "too little.")
public String[] createWordList(String text) {
return text.replaceAll("\\p{P}", "").toLowerCase().split(" ");
}
public Map<String, Integer> createWordCountMap(String[] wordArr) {
Map<String, Integer> wordCountMap = new TreeMap<>();
for (String word : wordArr) {
if(!wordCountMap.containsKey(word)){
wordCountMap.put(word, 1);
} else {
int count = wordCountMap.get(word);
wordCountMap.put(word, count + 1);
}
}
return wordCountMap;
}
String void displayCount(Map<String, Integer> wordCountMap) {
Iterator<Entry<String, Integer>> iter = wordCountMap.entrySet().iterator();
while(iter.hasNext()) {
Entry<String, Integer> entry = iter.next();
System.out.println(entry.getKey() + ": " + entry.getValue());
}
}
Now, after doing that, your main method looks more readable and your code is more reusable.
public static void main(String[] args) {
WordCount wc = new WordCount();
String text = "...";
String[] wordArr = wc.createWordList(text);
Map<String, Integer> wordCountMap = wc.createWordCountMap(wordArr);
wc.displayCount(wordCountMap);
}
UPDATE:
One small detail I forgot to mention is that, if instead of a TreeMap a HashMap is used, the output will come sorted by count value in descending order. This is because the hashing function will use value of the entry as the hash. Therefore, you won't need to "reverse" the map for this purpose. So, after switching to HashMap, the output should be as follows:
Output:
algorithms: 5
other: 2
for: 2
turn: 1
computer: 1
producing: 1
...
my suggestion is to use regexp and split and stream with grouping example 3
EX1 this solution does not use a collection LIST/MAP only array for me it is not optimal
#Test
public void testApp2() {
final String text = "Over time, computer engineers take advantage of each other's work and invent algorithms for new things. Algorithms combine with other algorithms to utilize the results of other algorithms, in turn producing results for even more algorithms.";
final String lowerText = text.toLowerCase();
final String[] split = lowerText.split("\\W+");
System.out.println("Output: ");
for (String s : split) {
if (s == null) {
continue;
}
int count = 0;
for (int i = 0; i < split.length; i++) {
final boolean sameWorld = s.equals(split[i]);
if (sameWorld) {
count = count + 1;
split[i] = null;
}
}
System.out.println(s + " " + count);
}
}
EX2 I think that's what you mean, but I'm not sure if I used too much for the list
#Test
public void testApp() {
final String text = "Over time, computer engineers take advantage of each other's work and invent algorithms for new things. Algorithms combine with other algorithms to utilize the results of other algorithms, in turn producing results for even more algorithms.";
final String[] split = text.split("\\W+");
final List<String> list = new ArrayList<>();
System.out.println("Output: ");
for (String s : split) {
if(!list.contains(s)){
list.add(s.toUpperCase());
final long count = Arrays.stream(split).filter(s::equalsIgnoreCase).count();
System.out.println(s+" "+count);
}
}
}
EX3 below is a test for your example but use MAP
#Test
public void test() {
final String text = "Over time, computer engineers take advantage of each other's work and invent algorithms for new things. Algorithms combine with other algorithms to utilize the results of other algorithms, in turn producing results for even more algorithms.";
Map<String, Long> result = Arrays.stream(text.split("\\W+")).collect(Collectors.groupingBy(String::toLowerCase, Collectors.counting()));
assertEquals(result.get("algorithms"), new Long(5));
System.out.println("Output: ");
result.entrySet().stream().forEach(x -> System.out.println(x.getKey() + " " + x.getValue()));
}
How do we get next key/value pair of a linkedhashmap using an enhanced for loop in Java?
What we want to achieve is to get a value from a linkedhashmap and pair it with the next value in the linkedhashmap. To achieve this, first of all, we have a list looking like the following:
LinkedHashMap<String, String> dates = new LinkedHashMap<String, String>();
// first quarter
dates.put("January", "2021-01-01");
dates.put("March", "2021-03-31");
// second quarter
dates.put("April", "2021-04-01");
dates.put("June", "2021-06-30");
// third quarter
dates.put("July", "2021-07-01");
dates.put("September", "2021-09-30");
// fourth quarter
dates.put("Oktober", "2021-10-01");
dates.put("December", "2021-12-31");
Above outputs:
{January=2021-01-01, March=2021-03-31, April=2021-04-01, June=2021-06-30, July=2021-07-01, September=2021-09-30, Oktober=2021-10-01, December=2021-12-31}
Then we want to select 2021-01-01 and then pair it with 2021-03-31. We then want 2021-04-01 and pair it with its next value, which is 2021-06-30 and so on... To achieve this, we have a function, where we initialize a enhanced for loop to get the values of the linkedhashmap. We use modulo to only select every second date starting from the first date, but we are only able to retrieve the first value, but not the second. Then using modulo, we select the third, but not the fourth value.
public void modtagMomsAngivelse() {
String serviceResponse = "";
int count = 0;
for (String name: dates.keySet()) {
if((count%2) == 0) {
String startDate = dates.get(name).toString();
// ----> String endDate = dates.next().get(name).toString(); <---- NEED SOMETHING LIKE THIS, BUT IS IT POSSIBLE?
system.out.println(startDate + " | " + endDate)
}
count++;
}
}
Above should output the following:
2021-01-01 | 2021-03-31
2021-04-01 | 2021-06-30
2021-07-01 | 2021-09-30
2021-10-01 | 2021-12-31
How can it be programmed correctly in Java?
You can't do this with an enhanced for loop. You have to use an Iterator directly:
Iterator<String> it = dates.keySet().iterator();
while (it.hasNext()) {
String startDateKey = it.next();
String endDateKey = it.next(); // Will fail if there isn't another element.
String startDate = dates.get(startDateKey).toString();
String endDate = dates.get(endDateKey).toString();
// ...
}
If dates isn't large, it may be easier to copy into a List, and then access by index:
List<String> keys = new ArrayList<>(dates.keySet());
for (int k = 0; k < keys.size(); k += 2) {
String startDateKey = keys.get(k);
String endDateKey = keys.get(k + 1); // Will fail if there isn't another element. Can change condition to `k + 1 < keys.size()`, if you want to miss off an unpaired element.
// ...
}
Really, a (LinkedHash)Map isn't an ideal structure to be holding together pairs of keys with semantic meaning. It would be better to store those keys together, as a single key object.
I'm new to java and I need to know How can i calculate distinctive number of words in HashMap
I got tweets and stored it into array of string like that
String [] words = {i, to , go , eat,know , i ,let , let , figure , eat};
HashMap <String,Integer> set=new HashMap();
for (String w:words)
{
int freq=set.get(w);
if (freq==null)
{
set.put(w1,1)
}
else
set.put(w1,freq+1)
}
let's suppose that HashMap now has all words that i need
now how can i calculate total of number of distinctive words ?
that i can see that words that have value = 1 in hashmap right ?
I tried to check
if (set.containsvalue(1))
int dist +=set.size();
but didn't work !
int dist = 0;
for (int i : set.values())
if (i == 1)
++dist;
Before you put a word into set, you should check if the key exists or not. If the key exists, then you should increase the value.
The following segment of your code is wrong:
int freq=set.get(w);
if (freq==null)
{
set.put(w1,1)
}
freq is declared to be an int to which null check cannot be applied. null checks are applicable to references.
Also, I think there is a typo that you are between w and w1
The correct code is:
String [] words = {i, to , go , eat,know , i ,let , let , figure , eat};
Map <String,Integer> set=new HashMap();
for (String w:words)
{
if (set.get(w)==null)
{
set.put(w,1)
}
else
set.put(w,set.get(w)+1)
}
Now if you iterate over the map to check the keys for which the value is 1, you will have your distinct words.
You have just to iterate through all the map and get the keys with freq == 1
int unique = 0;
for(String word : set.keySet()) {
int freq = set.get(word);
if(freq == 1) {
unique++;
}
}
System.out.println(unique);
Hi dear friends, I want to define a String array in java and use every
cells of that array in switch-case in java to count every string
elements.for example can you you help me to fix it thanks.
int i=20;
String [] str =new String[i];
string[0]="This";
string[1]="is";
string[2]="a";
string[3]="Test";
string[4]="This";
string[5]="This";
string[6]="a";
string[7]="a";
string[8]="a";
string[20]="Test";
switch(i)
{
case(0):this++
break;
case(1):is++
break;
case(2):a++
break;
case(3):test++
break;
}
Trying to enumerate all the different possible strings in the array is generally a bad idea and not the correct way to write your program. Sure your example works, but what would happen if your set of possible strings was not just {'this', 'is', 'a', 'test'}, but instead had say 10000 elements? What if you didn't know exactly what String elements were in the array? As a previous user mentioned, you want to use a HashMap<String, Integer>.
String[] arr = yourStringArray; //wherever your Strings are coming from
Map<String, Integer> strCounts = new HashMap<String, Integer>; //this stores the strings you find
for (int i = 0; i < arr.length; i++) {
String str = arr[i];
if (strCounts.containsKey(str)) {
strCounts.get(str) += 1; //if you've already seen the String before, increment count
} else {
strCounts.put(str, 1); //otherwise, add the String to the HashMap, along with a count (1)
}
}
While i don't really understand what you are trying to do, here are my two cents. You are probably getting an ArrayIndexOutOfBoundsException. This is because when you create an array of size 20, it can hold exactly 20 items. From range 0 to 19. So trying to do string[20]="Test"; will give an error because it's out of bounds.
I would do it this way:
int count;
String currentWord;
Map<String, Integer> wordMap = new HashMap<>();
StringTokenizer tokenizer = new StringTokenizer(mySentence);
while(tokenizer.hasNext()){
count = null;
currentWord = tokenizer.nextToken();
count = wordMap.get(currentWord);
if(count == null) wordMap.put(currentWord, 1);
else wordMap.put(currentWord, ++count);
}