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
Related
Hi I have this list below
String[] list = {"I","think","she","think","he","think","she","loves"};
I want to produce it like below. So the repeated words get increment.
["I","think","she","think(1)","he","think(2)","she(1)","loves"];
I've tried to explore this logic but I find it hard to add the increment number to my list, so far I'm only able to detect the repeated words. How can I add the number to the repeated words?
You can traverse the array and store each word with their number of occurrences in a Map object. As you traverse through and you find a word which is already present in the map then its simply means its a duplicate word.
EG:
Map<String, Integer> map = new HashMap<>();
String[] result = new String[list.length];
int i = 0;
for (String val : list) {
int count = map.getOrDefault(val, 0); // if map does not contain the key then the default occurrence is 0
result[i] = count > 0 ? val + "(" + count + ")" : val;
count++;
map.put(val, count);
i++;
}
Edit:
As mentioned by #Holger in the comments , a simplified for-loop.
for(String val : list) {
int count = map.merge(val, 1, Integer::sum) - 1;
result[i++] = count > 0 ? val + "(" + count + ")" : val;
}
Here is one possible solution:
String[] list = {"I", "think", "she", "think", "he", "think", "she", "loves"};
List<String> modified = new ArrayList<>();
Map<String, Integer> words = new HashMap<>();
for (String item : list) {
int val = words.getOrDefault(item, 0);
words.put(item, val + 1);
}
for (Map.Entry<String, Integer> entry : words.entrySet()) {
int val = entry.getValue();
String key = entry.getKey();
while (val > 0) {
if (val - 1 > 0) {
String newKey = key + "(" + (val - 1) + ")";
modified.add(newKey);
} else {
modified.add(key);
}
val--;
}
}
String[] result = modified.toArray(new String[0]);
System.out.println(Arrays.toString(result));
if you run this code, you should see output like this:
[think(2), think(1), think, she(1), she, loves, I, he]
class Test {
public static void main(String[] args) {
String[] list = { "I", "think", "she", "think", "he", "think", "she", "loves" };
List<String> finalList = new ArrayList<>();
for (int i = 0; i < list.length; i++) {
String elem = list[i];
long matchedCount = Arrays.asList(list).subList(0, i).parallelStream().filter(a -> a.equals(elem)).count();
if (matchedCount > 0) {
finalList.add(elem + "(" + matchedCount + ")");
} else {
finalList.add(elem);
}
}
System.out.println(finalList);
}
}
Output: [I, think, she, think(1), he, think(2), she(1), loves]
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;
}
How may I count all pairs of collisions in a list of Strings using hashcode of each string?
public class HashCollisions {
private static int strLength;
private static int colls;
public static void main(String[] args) {
String[] strings ={"AaAaAa","AaAaBB","AaBBAa","AaBBBB"};
strLength=strings.length;
for (int i = 0; i < strLength - 1; i++) {
for (int j = i + 1; j < strLength; j++) {
if (hash(strings[i]) == hash(strings[j]) && !(strings[i].equals(strings[j]))) {
colls++;
}
}
}
System.out.println(colls);
}
private static byte hash(String s) {
byte[] bytes = s.getBytes();
byte result = bytes[0];
for (int i = 1; i < bytes.length; i++) {
result ^= bytes[i];
}
return result;
}
}
With the given input I should detect the count of the pairs of collisions:
{0=[AaAaBB, AaBBAa], 32=[AaAaAa, AaBBBB]} which will be 2 .
Any other solution more efficient than O(n^2)?
You can group the list of strings by their hashCode and then work with the resulting map. As soon as you have more than one value for a given key there is
a collision:
public static void main(String[] args) {
List<String> strings = Arrays.asList("foo", "bar", "AaAa", "foobar",
"BBBB", "AaBB", "FB", "Ea", "foo");
Map<Integer, List<String>> stringsByHash = strings.stream()
.collect(Collectors.groupingBy(String::hashCode));
for (Entry<Integer, List<String>> entry : stringsByHash.entrySet()) {
List<String> value = entry.getValue();
int collisions = value.size() - 1;
if (collisions > 0) {
System.out.println(
"Got " + collisions + " collision(s) for strings "
+ value + " (hash: " + entry.getKey() + ")");
}
}
}
This prints:
Got 1 collision(s) for strings [foo, foo] (hash: 101574)
Got 1 collision(s) for strings [FB, Ea] (hash: 2236)
Got 2 collision(s) for strings [AaAa, BBBB, AaBB] (hash: 2031744)
Why don't you use a Set, put every value in your List into the Set, and find the number of collisions by calculating List.size() - Set.size()?
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;
}
}
I am a C++ programmer. Out of interest, I am developing a java application.
I have two strings in java:
String word1 = "Fold";
String word2 = "Flow";
Now I need a function to get the count of matching characters in both strings but those that are at different indexes. The strings could be of any length but always both words will be of the same length.
Added:
We should increment count for a character by that many occurrences in both words. Ex: abcd and xyaa should return 1, but abca and xaay should return 2. Hope it is clear now.
For ex:, the count for the above example should be 2 (Only letters 'o' and 'l' are considered. Though letter 'f' is present in both words, it is not considered since it is present at the same index on both strings.
My method was to create two map variables Map and initialize it with 0 for all characters. Then calculate count of how many times each letter occurs in both strings and finally check how many of these characters have count more than one.
Ex:
Map<Character, Integer> word_count_1 = createMap(); // initialize with a:0, b:0, c:0,...z:0
Map<Character, Integer> word_count_2 = createMap(); // initialize with a:0, b:0, c:0,...z:0
int count, value;
for (int i=0; i<word1.length(); i++)
{
if (word1.charAt(i) != word2.charAt(i))
{
value = word_count_1.get(word1.charAt(i));
word_count_1.put(word1.charAt(i), ++value);
value= word_count_2.get(word2.charAt(i));
word_count_2.put(word2.charAt(i), ++value);
}
}
Set set = word_count_2.entrySet();
Iterator i = set.iterator();
Map.Entry<Character, Integer> iter;
while(i.hasNext())
{
iter = (Map.Entry)i.next();
if ( (iter.getValue() > 0) && (word_count_1.get(iter.getKey())) > 0 )
{
count++; // This line has a bug. We shall ignore it for now
}
}
Is there any other better method to get the count instead of what I am trying to do? I just dont get a good feeling about what I have done.
Edited:
The line count++ (that I mentioned having a bug) should be changed to following to give correct result:
int letterCount1 = word_count_1.get(iter.getKey());
int letterCount2 = iter.getValue();
if ( (letterCount1 > 0) && (letterCount2 > 0 )
{
int minVal = letterCount1;
if (minVal > letterCount2)
minVal = letterCount2;
count+= minVal;
}
Java 8 Solution
public int duplicates(String wordOne, String wordTwo ){
Set<Character> charSet = new HashSet(109);
wordOne.chars().mapToObj(i -> (char)i).forEach(letter->charSet.add(letter));
int count = 0;
for(int i = 0; i < wordTwo.length(); i++)
if( charSet.contains(wordTwo.charAt(i)) && wordTwo.charAt(i) != wordOne.charAt(i) )
count++;
return count;
}
duplicates("Fold", "Flow"); // -> 2
There's nicer syntax to iterate over the set (see example below) but the actual counting looks fine.
Map<Character, Integer> word_count_1 = createMap(); // initialize with a:0, b:0, c:0,...z:0
Map<Character, Integer> word_count_2 = createMap(); // initialize with a:0, b:0, c:0,...z:0<Character, Integer>
int count, value;
for (int i=0; i<word1.length(); i++)
{
if (word1.charAt(i) != word2.charAt(i))
{
value = word_count_1.get(word1.charAt(i));
word_count_1.put(word1.charAt(i), ++value);
value= word_count_2.get(word2.charAt(i));
word_count_2.put(word2.charAt(i), ++value);
}
}
Set set = word_count_2.entrySet();
for(<Map.Entry<Character, Integer>> iter:set)
{
if ( (iter.getValue() > 0) && (word_count_1.get(iter.getKey())) > 0 )
{
count++; // This line has a bug. We shall ignore it for now
}
}
//Create set which contains word1's unique chars
Set<Character> word1Chars = new HashSet<>();
for(int i = 0; i< word1.length(); i++)
{
char ch = word1.charAt(i);
word1Chars.add(ch);
}
// Count how many chars in word2 are contained in word1 but in another position
int count = 0;
for(int i = 0; i < word2.length(); i++)
{
char ch = word2.charAt(i);
if(ch != word1.charAt(i) && word1Chars.contains(ch))
{
count++;
}
}
EDIT: You have to take into consideration that you may get a different counting depending on which word you iterate. E.g: "abc" and "daa"; "abc" has 1 but "daa" has 2.
If you want the total of correspondences in both words you need to modify this code accordingly.
You do not need to initialize maps for all the characters.
public static int matchCharCountInDifferentIndex(String word1, String word2) {
Map<Character, Integer> word_count_1 = new HashMap<>();
Map<Character, Integer> word_count_2 = new HashMap<>();
for (int i=0; i<word1.length(); i++)
{
if (word1.charAt(i) != word2.charAt(i))
{
word_count_1.compute(word1.charAt(i), (k, v) -> v == null ? 1 : v + 1);
word_count_2.compute(word2.charAt(i), (k, v) -> v == null ? 1 : v + 1);
}
}
int count = 0;
for (Map.Entry<Character, Integer> e : word_count_2.entrySet())
{
count += Math.min(e.getValue(), word_count_1.getOrDefault(e.getKey(), 0));
}
System.out.printf("word1=%s word2=%s result=%d%n", word_count_1, word_count_2, count);
return count;
}
Tests are
matchCharCountInDifferentIndex("Fold", "Flow"); // -> word1={d=1, l=1, o=1} word2={w=1, l=1, o=1} result=2
matchCharCountInDifferentIndex("abca", "xaay"); // -> word1={a=2, b=1, c=1} word2={a=2, x=1, y=1} result=2
In this code
map.compute(key, (k, v) -> v == null ? 1 : v + 1);
is equivalent to
map.put(key, map.getOrDefault(key, 0) + 1);
And
map.getOrDefault(key, 0)
is equivalent to
map.containsKey(key) ? map.get(key) : 0;