I was trying to solve some programs, I came across this interesting one, where we need to print the highest occurred character n times & likewise for other characters.
Ex: Input string : "please stay hydrated"
Output string : "aaaeeettyysplhdr"
I was only able to solve half way, where we print the highest occurred character & the times it has occurred using a HashMap.
public static void repeatedChar(String str) {
char[] chars = str.toCharArray();
Map<Character, Integer> map = new HashMap<>();
for (Character c : chars) {
if (map.containsKey(c)) {
map.put(c, map.get(c) + 1);
} else {
map.put(c, 1);
}
}
//Now To find the highest character repeated
int max = 0;
//setting to a by default
char maxCharacter = 'a';
for (Map.Entry<Character, Integer> entry : map.entrySet()) {
System.out.println("Key = " + entry.getKey() + ": Value " + entry.getValue());
if (max < entry.getValue()) {
max = entry.getValue();
maxCharacter = entry.getKey();
}
}
System.out.println("Max Character = " + maxCharacter + " Max Count : " + max);
}
This currently prints the highest occured character & the number of times that character has occurred. Can someone please let me know how to proceed further? Thanks
In order to get the desired output you need to sort your map by value. But since a hashmap is not meant to be sorted, but accessed fast, you could add all entries to a list and sort the list. Something like:
import java.util.Map.Entry;
import java.util.Comparator;
....
public static void repeatedChar(String str) {
char[] chars = str.toCharArray();
Map<Character, Integer> map = new HashMap<>();
for (Character c : chars) {
if (map.containsKey(c)) {
map.put(c, map.get(c) + 1);
} else {
map.put(c, 1);
}
}
//add map entries to list
List<Entry<Character, Integer>> list = new ArrayList<>(map.entrySet());
//sort entries by value descending
list.sort(Entry.comparingByValue(Comparator.reverseOrder()));
//print
for (Entry<Character, Integer> entry : list) {
for (int i = 0; i < entry.getValue(); i++){
System.out.print(entry.getKey());
}
}
}
and if you fancy a stream approach:
import java.util.Comparator;
import java.util.Map;
import java.util.function.Function;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
....
public static void repeatedCharWithStreams(String str) {
String output =
Pattern.compile("")
.splitAsStream(str)
.collect(Collectors.groupingBy(Function.identity(),Collectors.counting()))
.entrySet().stream()
.sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
.map(e -> e.getKey().repeat(e.getValue().intValue()))
.collect(Collectors.joining());
System.out.println(output);
}
Related
I have to create a program that counts the letters in string and I have a little problem with that.
This is my code in main:
Scanner sc = new Scanner(System.in);
String str;
int count;
System.out.println("Enter some text: ");
str = sc.nextLine();
char ch;
System.out.println("Letters: ");
for (ch = (char) 65; ch <= 90; ch++) {
count = 0;
for (int i = 0; i < str.length(); i++) {
if (ch == str.charAt(i) || (ch + 32) == str.charAt(i)) {
count++;
}
}
if (count > 0) {
System.out.println(ch + ": " + count);
}
}
Everything looks fine, but the output should not be in alphabetical order, rather ordered by the number of letters descending.
For example, if you input Hello World, the output should be something like this:
L: 3
O: 2
H: 1
D: 1
E: 1
R: 1
W: 1
The output would be sorted in descending order of letter frequency. That means the most frequent letter should appear first and the least last.
The order for letters that appears in equal proportions must be in alphabetical order.
The problem is that your outer loop browse the letters in alphabetical order, and that's where you display the count.
I would instead recommend browsing the input string with a single loop, updating the count of each letter in a Map<Character, Integer> as I encounter them.
Then once the input String has been consumed, I would sort the Map by descending values, and print each key/value pair.
Map<Character, Integer> lettersCount = new HashMap<>();
for (int i=0; i <str.length(); i++) {
Character current = str.charAt(i);
if (Character.isLetter(current)) {
Integer previousCount = lettersCount.get(current);
if (previousCount != null) {
lettersCount.put(current, previousCount + 1);
} else {
lettersCount.put(current, 1);
}
}
}
List<Map.Entry<Character, Integer>> list = new LinkedList<Map.Entry<Character, Integer>>( lettersCount.entrySet() );
Collections.sort( list, new Comparator<Map.Entry<Character, Integer>>()
{
public int compare( Map.Entry<Character, Integer> o1, Map.Entry<Character, Integer> o2 )
{
return (o2.getValue()).compareTo( o1.getValue() );
}
} );
for (Map.Entry<Character, Integer> entry : list) {
System.out.println(entry.getKey() + " : " + entry.getValue());
}
You can try it out on ideone.
As you can see, sorting a Map by values isn't trivial :-/
If you want to sort the results then you'll have to store the results & then iterate over them by their count descending to print in order
The best data structure to store them into would be a heap, keyed off of count. Java supplies such a data structure as java.util.PriorityQueue which can take a comparator function which first compares count & then character
https://docs.oracle.com/javase/7/docs/api/java/util/PriorityQueue.html
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String str;
int count;
System.out.println("Enter some text: ");
str = sc.nextLine();
char ch;
System.out.println("Letters: ");
LinkedHashMap<String, Integer> charCountMap = new LinkedHashMap<String, Integer>();
for (ch = (char) 65; ch <= 90; ch++) {
count = 0;
for (int i = 0; i < str.length(); i++) {
if (ch == str.charAt(i) || (ch + 32) == str.charAt(i)) {
count++;
}
}
if (count > 0) {
System.out.println(ch + ": " + count);
charCountMap.put(ch + "", count);
}
}
LinkedHashMap<String, Integer> sortedMapBasedOnValues = sortHashMapByValues(charCountMap);
for (Map.Entry<String, Integer> entry : sortedMapBasedOnValues.entrySet()) {
System.out.println("Key : " + entry.getKey() + " Value : " + entry.getValue());
}
}
// Following method used from
// http://stackoverflow.com/questions/8119366/sorting-hashmap-by-values
public static LinkedHashMap<String, Integer> sortHashMapByValues(LinkedHashMap<String, Integer> passedMap) {
List<String> mapKeys = new ArrayList<>(passedMap.keySet());
List<Integer> mapValues = new ArrayList<>(passedMap.values());
Collections.sort(mapValues, Collections.reverseOrder());
Collections.sort(mapKeys);
LinkedHashMap<String, Integer> sortedMap = new LinkedHashMap<>();
Iterator<Integer> valueIt = mapValues.iterator();
while (valueIt.hasNext()) {
Integer val = valueIt.next();
Iterator<String> keyIt = mapKeys.iterator();
while (keyIt.hasNext()) {
String key = keyIt.next();
Integer comp1 = passedMap.get(key);
Integer comp2 = val;
if (comp1.equals(comp2)) {
keyIt.remove();
sortedMap.put(key, val);
break;
}
}
}
return sortedMap;
}
}
The code below counts how many times the words and letters appeared in the string. How do I sort the output from highest to lowest? The output should be like:
the - 2
quick - 1
brown - 1
fox - 1
t - 2
h - 2
e - 2
b - 1
My code:
import java.util.HashMap;
import java.util.Map;
import java.util.StringTokenizer;
public class Tokenizer {
public static void main(String[] args) {
int index = 0;
int tokenCount;
int i = 0;
Map<String, Integer> wordCount = new HashMap<String, Integer>();
Map<Integer, Integer> letterCount = new HashMap<Integer, Integer>();
String message = "The Quick brown fox the";
StringTokenizer string = new StringTokenizer(message);
tokenCount = string.countTokens();
System.out.println("Number of tokens = " + tokenCount);
while (string.hasMoreTokens()) {
String word = string.nextToken().toLowerCase();
Integer count = wordCount.get(word);
Integer lettercount = letterCount.get(word);
if (count == null) {
// this means the word was encountered the first time
wordCount.put(word, 1);
} else {
// word was already encountered we need to increment the count
wordCount.put(word, count + 1);
}
}
for (String words : wordCount.keySet()) {
System.out.println("Word : " + words + " has count :" + wordCount.get(words));
}
for (i = 0; i < message.length(); i++) {
char c = message.charAt(i);
if (c != ' ') {
int value = letterCount.getOrDefault((int) c, 0);
letterCount.put((int) c, value + 1);
}
}
for (int key : letterCount.keySet()) {
System.out.println((char) key + ": " + letterCount.get(key));
}
}
}
You have a Map<String, Integer>; I'd suggest something along the lines of another LinkedHashMap<String, Integer> which is populated by inserting keys that are sorted by value.
It seems that you want to sort the Map by it's value (i.e., count). Here are some general solutions.
Specifically for your case, a simple solution might be:
Use a TreeSet<Integer> to save all possible values of counts in the HashMap.
Iterate the TreeSetfrom high to low.
Inside the iteration mentioned in 2., use a loop to output all word-count pairs with count equals to current iterated count.
Please see if this may help.
just use the concept of the list and add all your data into list and then use sort method for it
I am trying to count occurrence of each character in a string.
So if I input aaabbbccc I am expecting o/p as {a=3, b=3, c=3} but instead I keep on getting it as {a=1, b=1, c=1} as my hashtable contains method fails and returns false. What is wrong in the code?
I also know that there is a HashMap collection quite similar to hashtable. but as I am learing java I want to try the same code with all datastructures just to get an idea of methods. The code with map is not much different than what I am doing here still this code fails. and I am not able to figure out where is the bug in the code.
I have following code:
Hashtable<Character, Integer> stringHash = new Hashtable<Character, Integer>();
This stringHash is a class level variable.
for(int i=0; i<s.length(); i++){
if(stringHash ==null || stringHash.size()==0){
stringHash.put(s.charAt(i), 1);
}
else{
if(! stringHash.contains(s.charAt(i)) ){
System.out.println(s.charAt(i));
stringHash.put(s.charAt(i), 1);
}
else{
int count = stringHash.get(s.charAt(i));
stringHash.put(s.charAt(i), count++);
}
}
System.out.println(stringHash + " " + s.charAt(i) + " "+ stringHash.contains(s.charAt(i)));
}
This code works for me:
String s = "aaabbbccc";
Map<Character, Integer> stringHash = new HashMap<Character, Integer>();
for (char ch : s.toCharArray())
stringHash.put(ch, stringHash.containsKey(ch) ? (stringHash.get(ch) + 1) : 1);
System.out.println(stringHash);
// output: "{a=3, b=3, c=3}"
I am using a Map<K, V> instead of HashTable<K, V>, but this is more common.
Try something like this....The reason your code is failing is that you are checking contains() on HashTable instead of its keySet. Hope that helps
public static void main(String[] args) {
// TODO Auto-generated method stub
String s = "aaaaabbcccc";
Hashtable<Character, Integer> counter = new Hashtable<Character, Integer>();
int count = 0;
for(int i=0;i<s.length();i++){
if(!counter.keySet().contains(s.charAt(i))){
counter.put(s.charAt(i), 1);
} else {
count = counter.get(s.charAt(i));
counter.put(s.charAt(i), ++count);
}
}
for(char c:counter.keySet()) {
System.out.println("Character : "+c+" - Occurences : "+counter.get(c));
}
}
o/p
Character : b - Occurences : 2
Character : c - Occurences : 4
Character : a - Occurences : 5
Your code
if(stringHash ==null || stringHash.size()==0){
stringHash.put(s.charAt(i), 1);
}
would throw NPE if somehow the hashmap is null. Luckily it seems that you have initialized it properly. The block rather should have been
if(stringHash ==null){
stringHash = new HashMap()
stringHash.put(s.charAt(i), 1);
}
Again, that would not have fixed your bug. You should use containsKey instead of contains that checks for value in HashTable. What you are looking to implement can be summarized in following pseudocode.
initialize hashmap
for each character c in inputString
count = 0
if hashmap has a key for c
count = get value for c from hashmap
end if
put in to hashmap c, count + 1
end for
In Java this would look like :
Map<Character, Integer> charCountMap = new HashMap<>();
for(char c : inputString.toCharArray()){
int count = 0;
if(charCountMap.containsKey(c)){
count = charCountMap.get(c);
}
charCountMap.put(c,count+1);
}
Or for the adventurous, here is Java8 version
Map<Character,Long> map = s.chars().mapToObj(i->(char)i)
.collect(Collectors
.groupingBy(e -> e,
Collectors.counting()));
System.out.println(map);
Finally, do not use HashTable its a legacy class, no one uses it now a days. Stick with HashMap or other flavors of Map implementations.
Debug my code questions are discouraged, but in the way of solving the general problem of counting characters in a string I can suggest a much simpler method:
public static int[] countCharacters( String s ){
int[] count = new int[ 256 ];
for( int xChar = 0; xChar < s.length(); xChar++ ) count[ s.charAt( xChar ) ]++;
return count;
}
This assumes you have single byte characters.
Why do you use hashMap for counting character occurance?
I would use integer array of size 255 like so:
int[] counter = new int[256];
String s = "aaabbbcccc";
for(int i = 0; i < s.length(); i++){
counter[s.charAt(i)]++;
}
for(int i = 0; i < counter.length; i++)
if(counter[i] > 0)
System.out.println(((char)i) + " occurs " + counter[i] + " times");
that coude would give an output:
a occurs 3 times
b occurs 3 times
c occurs 4 times
Don't use Hashtable, you can simplify that code a lot, something like this should work:
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
public class Main {
public static void main(String[] args) {
Map<Character, Long> countMap = count("aabbbcccc");
for (Map.Entry<Character, Long> entry : countMap.entrySet()) {
System.out
.println(MessageFormat.format("Char ''{0}'' with count ''{1}''", entry.getKey(), entry.getValue()));
}
}
private static Map<Character, Long> count(String value) {
Map<Character, Long> countMap = new HashMap<Character, Long>();
if (StringUtils.isNotBlank(value)) {
for (int i = 0; i < value.length(); i++) {
Long count = countMap.get(value.charAt(i));
count = count == null ? 1 : count + 1;
countMap.put(value.charAt(i), count);
}
}
return countMap;
}
}
I want to display only the words that appear more than once in a string, single appearance of string should not be printed. Also i want to print strings whose length is more than 2 (to eliminate is,was,the etc)..
The code which I tried..prints all the strings and shows is occurrence number..
Code:
public static void main(String args[])
{
Map<String, Integer> wordcheck = new TreeMap<String, Integer>();
String string1="world world is new world of kingdom of palace of kings palace";
String string2[]=string1.split(" ");
for (int i=0; i<string2.length; i++)
{
String string=string2[i];
wordcheck.put(string,(wordcheck.get(string) == null?1: (wordcheck.get(string)+1)));
}
System.out.println(wordcheck);
}
Output:
{is=1, kingdom=1, kings=1, new=1, of=3, palace=2, world=3}
single appearance of string should not be printed...
also i want to print strings whose length is more than 2 (to eliminate is,was,the etc)..
Use it
for (String key : wordcheck.keySet()) {
if(wordcheck.get(key)>1)
System.out.println(key + " " + wordcheck.get(key));
}
Keeping track of the number of occurrences in a map will allow you to do this.
import java.util.HashMap;
import java.util.Map.Entry;
import java.util.Set;
public class Test1
{
public static void main(String[] args)
{
String string1="world world is new world of kingdom of palace of kings palace";
String string2[]=string1.split(" ");
HashMap<String, Integer> uniques = new HashMap<String, Integer>();
for (String word : string2)
{
// ignore words 2 or less characters long
if (word.length() <= 2)
{
continue;
}
// add or update the word occurrence count
Integer existingCount = uniques.get(word);
uniques.put(word, (existingCount == null ? 1 : (existingCount + 1)));
}
Set<Entry<String, Integer>> uniqueSet = uniques.entrySet();
boolean first = true;
for (Entry<String, Integer> entry : uniqueSet)
{
if (entry.getValue() > 1)
{
System.out.print((first ? "" : ", ") + entry.getKey() + "=" + entry.getValue());
first = false;
}
}
}
}
To get only the words occurring more then once, you have to filter your map.
Depending on your Java version you can use either this:
List<String> wordsOccuringMultipleTimes = new LinkedList<String>();
for (Map.Entry<String, Integer> singleWord : wordcheck.entrySet()) {
if (singleWord.getValue() > 1) {
wordsOccuringMultipleTimes.add(singleWord.getKey());
}
}
or starting with Java 8 this equivalent Lambda expression:
List<String> wordsOccuringMultipleTimes = wordcheck.entrySet().stream()
.filter((entry) -> entry.getValue() > 1)
.map((entry) -> entry.getKey())
.collect(Collectors.toList());
Regarding the nice printing, you have to do something similar while iterating over your result.
Use the below code
for (String key : wordcheck.keySet()) {
if(wordcheck.get(key)>1)
System.out.println(key + " " + wordcheck.get(key));
}
public static void main(String args[])
{
Map<String, Integer> wordcheck = new TreeMap<String, Integer>();
String string1="world world is new world of kingdom of palace of kings palace";
String string2[]=string1.split(" ");
HashSet<String> set = new HashSet<String>();
for (int i=0; i<string2.length; i++)
{
String data=string2[i];
for(int j=0;j<string2.length;j++)
{
if(i != j)
{
if(data.equalsIgnoreCase(string2[j]))
{
set.add(data);
}
}
}
}
System.out.println("Duplicate word size :"+set.size());
System.out.println("Duplicate words :"+set);
}
TreeMap.toString() is inherited from AbstractMap and the documentation states that
Returns a string representation of this map. The string representation consists of a list of key-value mappings in the order returned by the map's entrySet view's iterator, enclosed in braces ("{}"). Adjacent mappings are separated by the characters ", " (comma and space). Each key-value mapping is rendered as the key followed by an equals sign ("=") followed by the associated value. Keys and values are converted to strings as by String.valueOf(Object).
So better you write your own method that prints out the TreeMap in a way you want.
I have a hashmap storing the number of occurrences of character in a text. I am trying to print out the top 3 occurrences, but it is printing incorrectly.
int max = 1000000000;
for (int i = 1; i <= 3; i++) {
for (Character key : list.keySet()) {
if (list.get(key) < max) {
max = list.get(key);
System.out.println(i + ": " + key + " " + list.get(key));
break;
}
}
}
With Java 8 you can use the code below(*):
List<Entry<Character, Integer>> top3 = map.entrySet().stream()
.sorted(comparing(Entry::getValue, reverseOrder()))
.limit(3)
.collect(toList());
(*) with the following imports:
import static java.util.Comparator.comparing;
import static java.util.Comparator.reverseOrder;
import static java.util.stream.Collectors.toList;
You could modify your program to this form:
for (int i = 1; i <= 3; i++) {
int max = -1;
Character maxKey = 'a';
for (Character key : list.keySet()) {
if (list.get(key) > max) {
max = list.get(key);
maxKey = key;
}
}
System.out.println(i + ": " + maxKey + " " + max );
list.remove(maxKey);
}
You need to sort your entries by number of occurences and get the top 3:
List<Entry<Character, Integer>> entryList = new ArrayList<>(list.entrySet());
Collections.sort(entryList, new Comparator<Entry<Character, Integer>>(){
#Override
public int compare(Entry<Character, Integer> e1, Entry<Character, Integer> e2) {
return e2.getValue() - e1.getValue(); // descending order
}
});
// now let's get the top 3
List<Character> top3 = new ArrayList<>(3);
for(Entry<Character, Integer> e : entryList) {
top3.add(e.getValue());
if(top3.size() == 3) {
break;
}
}
Here's a solution using Java 8 streams, based on the one provided by #assylias. It performs the complete task of collecting character counts from a String into a Map and selecting the top 3 entries.
import java.util.ArrayList;
import static java.util.Comparator.*;
import java.util.List;
import java.util.Map.Entry;
import static java.util.stream.Collectors.*;
public class Stream {
public static void main(final String[] args) {
final String text = "hello stackoverflow, let's count these character occurrences!";
final char[] charArray = text.toCharArray();
final List<Character> characters = new ArrayList<Character>(text.length());
for (final char c : charArray) {
characters.add(c);
}
final List<Entry<Character, Long>> top3 = characters.stream()
.collect(groupingBy(Character::charValue, counting()))
.entrySet().stream()
.sorted(comparing(Entry::getValue, reverseOrder())).limit(3).collect(toList());
System.out.println(top3);
}
}
Output:
[e=8, c=7, =6]