Need help to sort and efficiently print the same occurrences of the words in the below string.
Here is the occurrence for the string: {java=2, occurences=1, program=3, sample=1, the=2}
Expected output:
java=2,occurences=1,sample=1,the=2
String str = "sample program java program the occurences the java program";
String[] inputstr = str.split(" ");
TreeMap<String,Integer> map = new TreeMap<>();
for(String input: inputstr) {
if(map.containsKey(input)) {
int value = map.get(input);
map.put(input,value+1);
} else {
map.put(input,1);
}
}
You can simply convert the above code to a single line using java-8
Map<String, Long> countMap = Arrays.stream(inputstr)
.collect(Collectors.groupingBy(Object::toString, Collectors.counting()));
EDIT :
We need to find values in the map that have an occurrence of more than one. Achieved so using the following code :
// inversed the map using "google-guava.jar"
Multimap<Long, String> inverseMap = HashMultimap.create();
for (Entry<String, Long> entry : countMap .entrySet()) {
inverseMap.put(entry.getValue(), entry.getKey());
}
for (Entry<Long, Collection<String>> entry : inverseMap.asMap().entrySet()) {
// split the values into an array
Object[] split = entry.getValue().stream().toArray();
if (split != null && split.length > 1) {
for (int j = 0; j < split.length; j++) {
System.out.println(String.valueOf(split[j]) + " : "
+ countMap.get(String.valueOf(split[j])));
}
}
}
Related
I do not fully understand how to return a 2D object. So I wrote a method that takes in an input with a document and I have to return a list of all unique words in it and their number of occurrences, sorted by the number of occurrences in a descending order. It is a requirement that I cannot control that this be returned as a 2-dimensional array of String.
So here is what I have so far:
static String[][] wordCountEngine(String document) {
// your code goes here
if (document == null || document.length() == 0)
return null;
Map<String, String> map = new HashMap<>();
String[] allWords = document.toLowerCase().split("[^a-zA-Z]+");
for (String s : allWords) {
if (map.containsKey(s)) {
int newVersion = (Integer.parseInt(map.get(s).substring(1, map.get(s).length())) + 1);
String sb = Integer.toString(newVersion);
map.put(s, sb);
} else {
map.put(s, "1");
}
}
String[][] array = new String[map.size()][2];
int count = 0;
for (Map.Entry<String, String> entry : map.entrySet()) {
array[count][0] = entry.getKey();
array[count][1] = entry.getValue();
count++;
}
return array;
}
I'm trying to use a HashMap to store the words and their occurrences. What is the best way to store key --> value pairs from a table into a String[][].
If the input is:
input: document = "Practice makes perfect. you'll only
get Perfect by practice. just practice!"
The output should be:
output: [ ["practice", "3"], ["perfect", "2"],
["by", "1"], ["get", "1"], ["just", "1"],
["makes", "1"], ["only", "1"], ["youll", "1"] ]
How do I store data like this in a 2D array?
String[][] simply is the wrong data structure for this task.
You should use a Map<String, Integer> map instead of <String, String> during the method run and simply return exactly that map.
This has multiple reasons:
you store integers as strings, and even do calculations by parsing the String to an integer again, calculating and then parsing back - bad idea.
The returned array does not guarantee the dimensions, there is no way to enforce that each sub-array has exactly two elements.
Note regarding your comment: if (for some reason) you need to convert the map to a String[][] you can certainly do that, but that conversion logic should be separated from the code generating the map itself. That way the code for wordCountEngine remains clean and easily maintainable.
Just because you need to return a particular typed data-structure does not mean you need to create similarly typed map inside your method. Nothing prevents you from using Map<String, Integer> and then converting it to String[][]:
Here is the code that does not use Java8 streeams:
static String[][] wordCountEngine(String document) {
// your code goes here
if (document == null || document.length() == 0)
return null;
Map<String, Integer> map = new HashMap<>();
for ( String s : document.toLowerCase().split("[^a-zA-Z]+") ){
Integer c = map.get(s);
map.put(s, c != null ? c + 1: 1);
}
String[][] result = new String[ map.size() ][ 2 ];
int count = 0;
for ( Map.Entry<String, Integer> e : map.entrySet() ){
result[count][0] = e.getKey();
result[count][1] = e.getValue().toString();
count += 1;
}
return result;
}
And for fun a Java8 version:
static String[][] wordCountEngine(String document) {
// your code goes here
if (document == null || document.length() == 0)
return null;
return Arrays
//convert words into map with word and count
.stream( document.toLowerCase().split("[^a-zA-Z]+") )
.collect( Collectors.groupingBy( s -> s, Collectors.summingInt(s -> 1) ) )
//convert the above map to String[][]
.entrySet()
.stream().map( (e) -> new String[]{ e.getKey(), e.getValue().toString() } )
.toArray( String[][]::new );
}
this is my Solution to Pramp's question although in C# I think it is the same Idea
[TestMethod]
public void PrampWordCountEngineTest()
{
string document = "Practice makes perfect. you'll only get Perfect by practice. just practice!";
string[,] result = WordCountEngine(document);
string[,] expected =
{
{"practice", "3"}, {"perfect", "2"},
{"makes", "1"}, {"youll", "1"}, {"only", "1"},
{"get", "1"}, {"by", "1"}, {"just", "1"}
};
CollectionAssert.AreEqual(expected,result);
}
public string[,] WordCountEngine(string document)
{
Dictionary<string, int> wordMap = new Dictionary<string, int>();
string[] wordList = document.Split(' ');
int largestCount = 0;
foreach (string word in wordList)
{
string lowerWord = word.ToLower(); // can't assing to the same variable
//remove special/punctuation characters
var sb = new StringBuilder();
foreach (var c in lowerWord)
{
if (c >= 'a' && c <= 'z')
{
sb.Append(c);
}
}
string cleanWord = sb.ToString();
if (cleanWord.Length < 1)
{
continue;
}
int count = 0;
if (wordMap.ContainsKey(cleanWord))
{
count = wordMap[cleanWord];
count++;
}
else
{
count = 1;
}
if (count > largestCount)
{
largestCount = count;
}
wordMap[cleanWord] = count;
}
// we have a list of all of the words in the same length in a given cell of the big list
List<List<string>> counterList = new List<List<string>>();
for (int i = 0; i < largestCount + 1; i++)
{
counterList.Add(new List<string>());
}
foreach (var word in wordMap.Keys)
{
int counter = wordMap[word];
counterList[counter].Add(word);
}
string[,] result = new string[wordMap.Keys.Count,2];
int key = 0;
//foreach list of words with the same length we insert the count of that word into the 2D array
for (var index = counterList.Count-1; index > 0; index--)
{
var list = counterList[index];
List<string> wordListCounter = list;
if (wordListCounter == null)
{
continue;
}
foreach (var word in wordListCounter)
{
result[key, 0] = word;
result[key, 1] = index.ToString();
key++;
}
}
return result;
}
I have a Map with key as String and value as List as below
<Key>Path1 Value=[164,123,111,131]
<Key>Path2 Value=[164,122,135,133]
<Key>Path3 Value=[190,144,100,126]
<Key>Path4 Value=[113,122,453,117]
I want to compare each Key's Value with other Key's Value like Path1 Value with rest of Path's values and so on,
and also no duplicate comparision should happen, like if Path1 value is compared in 1st iteration . It should not compare Path2 with Path1 in 2nd iteration.
Am stuck with this problem . Please help me with any solution. Thanks in advance .
I have started with following code :
for (Map.Entry<String, List<String>> entry : map1.entrySet()) {
String key = entry.getKey();
for (String val : entry.getValue()) {
// do something with key and each val
}
}
Only compare keys where the first is less than the second, or some similar simple strategy.
for (String key1 : map.keySet()) {
for (String key2 : map.keySet()) {
if (key1.compareTo(key2) < 0) {
// compare key1 to key2
}
}
}
Maybe, this would be a better strategy than one suggested by Peter Lawrey cause it's not O(N*N)
Map<String, List<String>> map = new HashMap<>();
map.put("Path1", new ArrayList<>(Arrays.asList("164","123","111","131")));
map.put("Path2", new ArrayList<>(Arrays.asList("164","122","135","133")));
map.put("Path3", new ArrayList<>(Arrays.asList("190","144","100","126")));
map.put("Path4", new ArrayList<>(Arrays.asList("113","122","453","117")));
List<String> list = new LinkedList<>(map.keySet());
for (int i = 0; i < list.size(); i++) {
for (int j = i + 1; j < list.size(); j++) {
List<String> temp = new ArrayList<>(map.get(list.get(i)));
if (temp.removeAll(map.get(list.get(j)))) {
// do what you want
System.out.println(list.get(i) + " has duplicates with " + list.get(j));
}
}
}
I am new to Android and Java programming. I have a problem with this method.
I am trying to count how many times the same strings appeared.
For example: input in list l, output in list m
List l:
string1
string1
string2
string2
string2
List m:
2x string1
3x string2
My lists:
List<String> l = new ArrayList<>();
List<String> m = new ArrayList<>();
My method:
public String Generuj() {
String generator = "";
int x = 0;
int j = 0;
if (l.size() > 0) {
m.add(l.get(0));
for (int i = 1; i < l.size(); i++) {
while (j < l.size() && m.get(m.size() - 1).equals(l.get(j))) {
x++;
j++;
}
m.add("\n" + x + "x " + l.get(i));
}
}
for (int i = 0; i < m.size(); i++) {
generator = generator.concat(m.get(i));
}
return generator;
}
Thank you in advance for any help.
Modified OP's solution: (Without HashMap)
public String Generuj() {
String generator = "";
int x = 0;
while(l.size() > 0) {
String someString=l.remove(0);
x=0;
for (int i = 0; i < l.size();) {
if(l.get(i).equals(someString)){
x++;
l.remove(i);
}else{
i++;
}
}
m.add("\n" + x + "x " + someString);
}
for (int i = 0; i < m.size(); i++) {
generator = generator.concat(m.get(i));
}
return generator;
}
As wanpanman as say you can use hashmap :
private void countEachString(List<String> strings) {
HashMap<String, Integer> stringCountMap = new HashMap<>();
for (String string : strings) {
if (!stringCountMap.containsKey(string)) stringCountMap.put(string, 0);
stringCountMap.put(string, stringCountMap.get(string) + 1);
}
print(stringCountMap);
}
private void print(HashMap<String, Integer> hashMap) {
for (String string : hashMap.keySet()) {
Log.d("print out", hashMap.get(string) + " x " + string);
}
}
you can change the print for whatever you want
I would recommend using a HashMap< String, Integer > rather than simply prepending an x. By doing something like this:
HashMap< String, Integer > countMap = new HashMap<>();
for( String s : l )
{
if( countMap.containsKey( s ) )
{
// Add one to the string's occurrence.
countMap.put( s, countMap.get( s ) + 1 );
}
else
{
// Set the string's first occurrence.
countMap.put( s, 1 );
}
}
You have the count of all the occurrences of all the strings in the input list. If you still need to print out the results, you can iterate through that HashMap's EntrySet and concatenate from there:
generator.concat( entry.getValue() + "x " + entry.getKey() );
In Java 8 you can easily do it with streams:
List<String> result = l.stream() // stream list l
.collect(
Collectors.groupingBy( // collect list elements to map grouping by keys and counting values
Function.identity(),
Collectors.counting()
)
)
.entrySet() // get map's entry set
.stream() // and stream it
.map(entry -> entry.getValue() + "x " + entry.getKey()) // map each map entry to result string
.collect(Collectors.toList()); // collect the results to new list
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 string but cannot parse it into Map
string s="sakib hasan : 3 : sumon ali : 4 : tutul : 100
I need to create a HashMap from above string. sakib hasan,sumon ali,tutul,shila akter should be KEY of HashMap and 3,4,100,1, should be VALUEs of KEYs.
I have tried with the flowing code but unable to solve the problem
Map<String, Integer>amap=new HashMap<String,Integer>();
String[] splt=s.split(":");
for (String string : splt)
{
String[] pair=string.split(" ");
amap.put(pair[0])+Integer.parseInt(pair[1]);
}
Is there a way can I do this without hard coding
Try this.
Your split on ":" will return each item individually.
Then you just have to take each group as a set of two which you can account for in the for loop with i+=2
Map < String, Integer > amap = new HashMap < String, Integer > ();
String[] splt = s.split(" : ");
for (int i = 0; i < splt.length; i += 2) {
amap.put(splt[i],Integer.parseInt(splt[i + 1]));
}
In your code, your for loop is going through each element that you split and every single time, adding the hashmap only index 0 & 1. You need to increment the indices.
String s = "sakib hasan : 3 : sumon ali : 4 : tutul : 100 : shila akter : 1";
Map<String, Integer> amap = new HashMap<String, Integer>();
String[] splt = s.split(":");
for(int i = 1; i < splt.length;i+=2)
amap.put(splt[i-1].trim(), Integer.parseInt(splt[i].trim()));
Here is a similar solution using streams instead of a for loop:
IntStream.range(0, splt.length / 2)
.forEach(i -> map.put(splt[i], Integer.parseInt(splt[i + 1]));
Or you could turn the .forEach into a collector that creates the map.
At least,I got the answer
Map<String, Integer>amap=new HashMap<String,Integer>();
String[] splt=s.split(":");
try {
for (int i = 0; i <= pair.length; i +=2) {
amap.put(pair[i].trim(), Integer.parseInt(pair[(1 + i)].trim()));
}
} catch (Exception e) {
}