I have written a code snippet to read a sentence and print the words in the sentence along with their count of occurence.
Example:
String = Java is a language. java is easy and i like Java
Expected output :
Java =3, is=2 a=1, language=1, easy=1, and=1 i=1, like=1
I want to achieve it by using two nested for loops but I am missing something and the code is broken. Here is the snippet
package corejava;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
public class DuplicateStringOccurence {
public static void main(String[] args) {
// TODO Auto-generated method stub
String myString = " Java is a language. java is easy and i like Java";
String[] wordsInMySentence = getWords(myString);
Map<String, Integer> myMap = new HashMap<String, Integer>();
int countOfOccurence = 1;
// outloop i, innerlop j
for(int i=0;i<wordsInMySentence.length;i++) {
if (myMap.containsKey(wordsInMySentence[i])) {
countOfOccurence=1;
continue;
}
for(int j=i+1;j<wordsInMySentence.length;j++)
{
if (wordsInMySentence[i].equalsIgnoreCase(wordsInMySentence[j])) {
// match found
countOfOccurence++;
}
myMap.put(wordsInMySentence[i], countOfOccurence);
}
}
// print the duplicates and counts
for (Entry<String, Integer> entry : myMap.entrySet()) {
System.out.println(entry.getKey() + ":" + entry.getValue().toString());
}
}
private static String[] getWords(String myString) {
// TODO Auto-generated method stub
String[] wordsInMySentence = myString.replaceAll("[^a-zA-Z ]", "").toLowerCase().split("\\s+");
// create the array of words from the sentence
for (String s:wordsInMySentence) {
// System.out.println(s);
}
return wordsInMySentence;
}
}
I am not getting the expected output. I want to correct this piece of code . Can someone guide what is the mistake here?
Using java8 you can do like below :
First split you string by using regex ("[. ]+") and store that into List.
Then using Collectors.toMap ,
toMap(Function<? super T,? extends K> keyMapper, Function<? super T,? extends U> valueMapper,BinaryOperator<U> mergeFunction)
It takes below three arguments :
KeyMapper - k -> k.toLowerCase()
ValueMapper - k -> 1
MergeFunction - Here Integer::sum
You can refer doc Collectors.toMap(keyMapper,valueMapper,mergeFunction)
public class WordCount {
public static void main(String[] args) {
String sentense= "Java is a language.java is easy and i like Java";
List<String> list = Stream.of(sentense).map(k -> k.split("[. ]+")).flatMap(Arrays::stream)
.collect(Collectors.toList());
Map<String, Integer> countMap= list.stream()
.collect(Collectors.toMap(k -> k.toLowerCase(), k -> 1, Integer::sum));
System.out.println(countMap);
// Output : {a=1, java=3, like=1, and=1, i=1, language=1, is=2, easy=1}
}
}
With Map data structure you need only one for loop. Here is one solution using basic for loop:
String sentense= "Java, is a language.java is easy and i like Java";
String[] words = sentense.split("\\W+");
Map<String, Integer> countMap = new HashMap<>();
for(String word : words) {
word = word.toLowerCase();
Integer count = countMap.containsKey(word)? countMap.get(word) + 1 : 1;
countMap.put(word, count);
}
System.out.println(countMap);
You can use the handy merge method to keep track of the sums
private static String[] getWords(String myString) {
return myString.trim().split("\\W+");
}
public static void main(String[] args) {
String myString = "\tJava is a language. java is easy and i like Java ";
Map<String, Integer> myMap = new HashMap();
for (String word : getWords(myString)) {
myMap.merge(word.toLowerCase(), 1, Integer::sum);
}
System.out.println(myMap);
}
Related
Here is my code
I've researched on how to find how many duplicates of strings are in a string array. I think I have done this correctly but the method I'm using requires me to return a string value, but it will not let me return the value. In short, I am trying to find the food that most patients w/ "food poisoning" have eaten recently. The "common food" needs to be the value in the array when it's been repeated 5 times. Any suggestions?
You can use strems to find the most common element in array
import java.util.Comparator;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class Main {
public static void main(String[] args) {
String[] data = {"A", "A", "A", "B", "B", "C"};
Map.Entry<String, Long> entry = Stream.of(data)
.collect(Collectors.groupingBy(w -> w, Collectors.counting()))
.entrySet()
.stream()
.max(Comparator.comparing(Map.Entry::getValue))
.orElse(null);
if (entry != null) {
System.out.println(entry.getKey());
System.out.println(entry.getValue());
}
}
}
In these situation you can use maps for both optimization and usability.
Below is your method. I did not write all data.
I have created a foodMap.
static String mostCommonFood() {
String data[] = {"a","b"};
Map<String, Integer> foodMap = new HashMap<>();
for (String val : data) {
if (foodMap.containsKey(val)) {
foodMap.put(val, foodMap.get(val)+1);
} else {
foodMap.put(val, 1);
}
}
String mostCommonFood = "";
int mostCommonFoodValue = 0;
for(Map.Entry<String, Integer> foods : foodMap.entrySet()) {
if(foods.getValue() > mostCommonFoodValue) {
mostCommonFoodValue = foods.getValue();
mostCommonFood = foods.getKey();
}
}
return mostCommonFood;
}
I have a scenario where I need to take the keys of a Map<String, Set<String>>, and add them into a new Set<String> that is sorted. The sort order is based on the Map values for each key. The value for each key of the map is a Set containing other keys that are related to that key.
I need the keys to be sorted in such a way that a related key must be BEFORE another key that contains it in its related Set. To use a programming paradigm, it's similar to requiring a variable to be declared on an earlier line, before it can be referenced on another line.
For example, the following represents the contents of the Map<String, Set<String>>:
abc=[def, ghi, jkl, mno]
def=[]
ghi=[def]
jkl=[ghi, stu]
mno=[]
pqr=[abc]
stu=[def]
vwx=[mno, ghi]
zy0=[jkl]
In this example, the key "jkl" has a relationship to keys, "ghi" and "stu", "def" does not have a relationship to any of the keys.
NOTE: The relationships will be ONE-WAY only. So, for example, if "ghi" is related to "def", "def" will NEVER be related to "ghi".
So, for the above Map, the sort order would be:
def=[]
mno=[]
ghi=[def]
stu=[def]
vwx=[mno, ghi]
jkl=[ghi, stu]
zy0=[jkl]
abc=[def, ghi, jkl, mno]
pqr=[abc]
Here's the Comparator that I wrote. It's inside of a runnable test class that uses the example above:
import java.util.*;
public class RelationshipComparator_Test {
public static void main(String[] args) {
String[] testMap = "abc=[def,ghi,jkl,mno]|def=[]|ghi=[def]|jkl=[ghi,stu]|mno=[]|pqr=[abc]|stu=[def]|vwx=[mno,ghi]|zy0=[jkl]".split("[|]");
Map<String, Set<String>> relationshipMap = new HashMap<>();
for (String entry : testMap) {
String[] keyValue = entry.split("[=]");
String replacement = keyValue[1].replaceAll("[^a-z0-9,]", "");
Set<String> valueSet = new HashSet<>();
String[] values = (!replacement.equals("") ? replacement.split("[,]") : new String[0]);
Collections.addAll(valueSet, values);
relationshipMap.put(keyValue[0], valueSet);
}
Set<String> sortedKeys = new TreeSet<>(new RelationshipComparator(relationshipMap));
sortedKeys.addAll(relationshipMap.keySet());
for (String key : sortedKeys) {
System.out.println(key + "=" + relationshipMap.get(key));
}
}
static class RelationshipComparator implements Comparator<String> {
private Map<String, Set<String>> relationshipMap;
RelationshipComparator(Map<String, Set<String>> relationshipMap) {
this.relationshipMap = relationshipMap;
}
#Override
public int compare(String o1, String o2) {
Set<String> o1Set = relationshipMap.get(o1);
Set<String> o2Set = relationshipMap.get(o2);
if (o1Set != null && o2Set != null) {
if (o1Set.size() == 0 && o2Set.size() > 0) {
printCompare(o1, o2, "o1Set.size() == 0: -1");
return -1;
}
if (o2Set.size() == 0 && o1Set.size() > 0) {
printCompare(o1, o2, "o2Set.size() == 0: 1");
return 1;
}
if (o1Set.contains(o2)) {
printCompare(o1, o2, "o1Set.contains(o2): 1");
return 1;
}
if (o2Set.contains(o1)) {
printCompare(o1, o2, "o2Set.contains(o1): -1");
return -1;
}
}
printCompare(o1, o2, "default: " + o1.compareTo(o2));
return o1.compareTo(o2);
}
private void printCompare(String o1, String o2, String result) {
System.out.println("**********");
System.out.println("o1: " + o1 + "=" + relationshipMap.get(o1));
System.out.println("o2: " + o2 + "=" + relationshipMap.get(o2));
System.out.println("result: " + result);
System.out.println("**********");
System.out.println();
}
}
}
If you run the code, you'll see the following output:
def=[]
mno=[]
ghi=[def]
jkl=[stu, ghi]
abc=[def, ghi, jkl, mno]
pqr=[abc]
stu=[def]
vwx=[ghi, mno]
zy0=[jkl]
It's incorrect because, "jkl" references "stu", but "stu" is sorted after "jkl".
Any help would be greatly appreciated.
You say that relationships are one-way, which rules out obvious cases such as:
a=[b]
b=[a]
for which no solution is possible. However, we also need to rule out cyclic relationships such as:
a=[b]
b=[c]
c=[a]
If this is the case then I believe you can achieve the required ordering by using a PriorityQueue to order keys by the size of the value set related to the key. As keys are removed from the queue they also have to be removed from any of the related value sets that contain them. Which value sets contain a given key can be recovered from a reverse Map<String, Set<String>> which holds the set of keys that refer to a given value key.
Hopefully some code will make things clearer:
static List<String> orderByRef(Map<String, Set<String>> relationshipMap)
{
final Map<String, Set<String>> relationshipMapCopy = new HashMap<>();
for(String key : relationshipMap.keySet())
relationshipMapCopy.put(key, new HashSet<>(relationshipMap.get(key)));
final Map<String, Set<String>> referencedBy = new HashMap<>();
for(String key : relationshipMap.keySet())
referencedBy.put(key, new HashSet<>());
for (Entry<String,Set<String>> e : relationshipMapCopy.entrySet())
for(String v : e.getValue())
referencedBy.get(v).add(e.getKey());
PriorityQueue<String> pq = new PriorityQueue<>(new Comparator<String>()
{
#Override
public int compare(String k1, String k2)
{
return relationshipMapCopy.get(k1).size() - relationshipMapCopy.get(k2).size();
}
});
pq.addAll(relationshipMap.keySet());
List<String> orderedKeys = new ArrayList<>();
while(!pq.isEmpty())
{
String minKey = pq.poll();
if(!relationshipMapCopy.get(minKey).isEmpty())
{
// cyclic relationship
break;
}
orderedKeys.add(minKey);
for(String refKey : referencedBy.get(minKey))
{
// remove minKey from value set of refKey
relationshipMapCopy.get(refKey).remove(minKey);
// reorder refKey in pq
pq.remove(refKey);
pq.add(refKey);
}
}
return orderedKeys;
}
Note that since we're modifying the relationshipMap by removing keys from value sets we first need to create a deep copy. Also, we can detect the presence of a cyclic relationships by checking that the value set of the min key is empty.
Output:
def []
mno []
stu [def]
ghi [def]
vwx [ghi, mno]
jkl [stu, ghi]
zy0 [jkl]
abc [def, ghi, jkl, mno]
pqr [abc]
Which satisfies the constraint that no key is referenced before it appears in the list.
For input containing a cyclic relationship, eg (z=[y]|y=[]|a=[b]|b=[c]|c=[a]), we get:
y []
z [y]
I'm studying for Java 8 Lambda and Unary Functional Interface. I have a practice assignment about "Function" class using HashMap, which the following steps to do:
Create a variable of type Function<Set, Map> that receives a Set and creates a HashMap using lambda expressions
Put words in the map, using as key the uppercase first letter of that word
Execute lambda expression and view the result
I trying in the following way, but it doesn't work. I think that the problem is in the lambda expression, but I want to understand how I have to do (for simplicity I put the same word as key). In this way, the result is "null".
import java.util.*;
import java.util.function.Function;
public class FunctionTest {
public static void main(String[] args) {
HashSet<String> hs = new HashSet<String>();
hs.add("ciao");
hs.add("hello");
hs.add("hallo");
hs.add("bonjour");
Function<Set, Map> setToMap = s2 -> (Map) new HashMap().put(s2,s2);
System.out.println(setToMap.apply(hs));
}
}
For the above example, the expected result should be {B=bonjour, C=ciao, H=hello}.
I think this means that you have to add all the words of the Set in the Map following 2 rules
the key is the first letter in uppercase
the value is the word
Function<Set<String>, Map<Character, String>> setToMap = aSet -> {
Map<Character, String> map = new HashMap<>();
for (String s : aSet ) {
map.put(s.toUpperCase().charAt(0), s);
}
return map;
};
// or using Streams :
Function<Set<String>, Map<Character, String>> setToMap = aSet ->
aSet.stream().collect(Collectors.toMap(s -> s.toUpperCase().charAt(0),
Function.identity(),
(oldK, newK) -> oldK)); // merging function in cas of duplicate key
Tip: don't use raw types, but specify them as much as possible:
Function<Set,Map> becomes Function<Set<String>, Map<Character, String>>
I bet that you misunderstood your problem a bit.
You probably want a function that gets the key from the value of each item you have in the set. So:
Set<String> set = new HashSet<>();
set.add("ciao");
set.add("hello");
set.add("bonjour");
Function<String, Character> keyExtractor = s -> Character.toUpperCase(s.charAt(0));
Map<Character, String> map = set.stream()
.collect(Collectors.toMap(keyExtractor, Function.identity()));
This assumes you only have one word for each letter.
If you want to have more than one entry for each first letter then you can do:
Set<String> set = new HashSet<>();
set.add("ciao");
set.add("hello");
set.add("hallo");
set.add("bonjour");
Function<String, Character> keyExtractor = s -> Character.toUpperCase(s.charAt(0));
Map<Character, List<String>> map = set.stream()
.collect(Collectors.groupingBy(keyExtractor));
If you wanted to do it without streams, it will be more complicated but possible:
Function<Set<String>, Map<Character, List<String>>> setConverter = set -> {
Map<Character, List<String>> map = new HashMap<>();
for (String s : set) {
Character key = Character.toUpperCase(s.charAt(0));
map.compute(key, (k, v) -> {
if (v == null) {
List<String> newList = new ArrayList<>();
newList.add(s);
return newList;
} else {
v.add(s);
return v;
}
});
}
return map;
};
public class Main {
public static void main(String[] args) {
HashSet<String> hs = new HashSet<String>();
hs.add("ciao");
hs.add("hello");
hs.add("hallo");
hs.add("bonjour");
//System.out.println(setToList.apply(hs));
Function<Set<String>, Map<String,String>> setToMap = s2 -> {
HashMap<String, String> map = new HashMap<>();
for ( String o : s2)
{
map.put(o.toUpperCase(), o);
}
return map;
};
System.out.println(setToMap.apply(hs));
}
public class FunctionTest {
public static void main(String[] args) {
HashSet<String> hs = new HashSet<String>();
hs.add("ciao");
hs.add("hello");
hs.add("hallo");
hs.add("bonjour");
Function<Set<String>,Map> function=set ->{
Map<String,String> mapSet=new HashMap<>();
set.forEach(valueOfSet->mapSet.put(valueOfSet.substring(0,1).toUpperCase(),valueOfSet));'
return mapSet;
};
System.out.println(function.apply(hs));
}
}
Without Using Function you can do it as below:
Map<String,String> mapSet=new HashMap<>();
hs.forEach(value->mapSet.put(value.substring(0,1),value));
System.out.println(mapSet);
I'm new to Java, so sorry if this is pretty obvious, but I can't quite understand how to work with 2 HashMaps inside each other
I have my main, where I want to add some words to a Map, and then, I want to read them:
public static void main(String[] args) {
Dicionario d = new Dicionario();
d.add("english", "BOOK", "Book");
d.add("french", "BOOK", "livre");
d.add("portuguese", "BOOK", "livro");
d.add("english", "YEAR", "year");
d.add("french", "YEAR", "an");
d.add("portuguese", "YEAR", "ano");
System.out.println(d);
}
This Map, has another Map inside him:
private Map<String, Map<String, String> > dic = new HashMap<>();
Then I add those words:
protected void add(String s1, String s2, String s3){
Map<String, String> m = new HashMap<>();
m.put(s2, s3);
dic.put(s1, m);
}
And redefine the function toString to read them, but only appears 1 value per key:
#Override
public String toString(){
String s= "";
for(Map.Entry<String, Map<String,String>> entry : dic.entrySet())
{
s += "\"" + entry.getKey() + "\": ";
for(Map.Entry<String, String> entry2 : dic.get(entry.getKey()).entrySet())
{
s+=entry2.getKey() + "->" + entry2.getValue() + "\t";
}
s+="\n";
}
return s;
}
Why is that? I am looking at this like if it was a bidimensional array, but with 2 values (key, value) in each position.
How can I do to show all the values that the keys from the first map have?
Thanks, and sorry for such a basic question.
You need to modify your add method to following
protected void add(String s1, String s2, String s3) {
Map<String, String> m = null;
m = dic.get(s1);
if (m == null) {
m = new HashMap<>();
}
m.put(s2, s3);
dic.put(s1, m);
}
The problem is that in your add(String, String, String) method, you are instancing a new HashMap each time so you overwrite the previously instanced HashMap from a previous call.
You should update your method this way:
protected void add(String s1, String s2, String s3){
Map<String, String> m = dic.get(s1);
if (m == null) {
m = new HashMap<>();
dic.put(s1, m);
}
m.put(s2, s3);
}
To avoid having to manage this by hand yourself, I suggest that you use Guava's Table data structure (more specifically HashBasedTable).
I was working on a program to validate that a map contains some set of values or not. Map API is having one method called as map.containsValue(string). But, this method verify complete String as value.
import java.util.HashMap;
import java.util.Map;
public class TestMap {
/**
* #param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Map<Integer, String> map = new HashMap<Integer, String>();
map.put(6, "P_T");
map.put(7, "P_Q_T");
map.put(8, "T");
map.put(9, "A");
map.put(10, "B");
map.put(11, "P_A");
map.put(1, "P_1");
map.put(2, "Q");
map.put(3, "P_Q");
map.put(4, "Q_T");
map.put(5, "T");
System.out.println("Map is = "+map);
System.out.println("Result is = " + map.containsValue("P"));
}
}
Above program will return output as :
Map is = {1=P_1, 2=Q, 3=P_Q, 4=Q_T, 5=T, 6=P_T, 7=P_Q_T, 8=T, 9=A, 10=B, 11=P_A}
Result is = false
My requirement is that Result should be true as, Map contains keys 1, 3, 6, 7, 11, which contains char P as value.
I have a solution, that i could use loop to verify each value and then find indexOf each value for P char.
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
public class TestMap {
/**
* #param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Map<Integer, String> map = new HashMap<Integer, String>();
map.put(6, "P_T");
map.put(7, "P_Q_T");
map.put(8, "T");
map.put(9, "A");
map.put(10, "B");
map.put(11, "P_A");
map.put(1, "P_1");
map.put(2, "Q");
map.put(3, "P_Q");
map.put(4, "Q_T");
map.put(5, "T");
System.out.println("Map is = " + map);
System.out.println("Result is = " + map.containsValue("P"));
System.out.println("Result from loop is = " + verifyMap(map));
}
private static boolean verifyMap(Map<Integer, String> map) {
// TODO Auto-generated method stub
Set<Integer> set = map.keySet();
Iterator<Integer> itr = set.iterator();
while (itr.hasNext()) {
Integer integer = (Integer) itr.next();
String str = map.get(integer);
if (str.indexOf("P") >= 0)
return true;
}
return false;
}
}
I have evaluated string with indexOf method instead of contains, which is marginally faster .
See this question
This return the desired result as:
Result from loop is = true
But, I just want to know, is there any other way to verify same?
You can write your method with less code using Java 8 Streams :
private static boolean verifyMap(Map<Integer, String> map) {
return map.values().stream().anyMatch(str->str.indexOf("P") >= 0);
}
Even in Java 7 your method can be shorter :
private static boolean verifyMap(Map<Integer, String> map) {
for (String str : map.values()) {
if (str.indexOf("P") >= 0)
return true;
}
return false;
}
You can also try this:
private static boolean verifyMap(Map<Integer, String> map) {
for(String val:map.values())
{
if( val.contains("P"))
{
return true;
}
}
return false;
}
I was able to find out a way by which I could verify partial char in the values of map.
I have used Matcher API from Java-7. Thanks to #Eran, for a hint related to Java8.
Below is the code snippet, i am using now.
private static boolean verifyMap(Map<Integer, String> map) {
Matcher matcher = Pattern.compile(".*p.*", Pattern.CASE_INSENSITIVE)
.matcher(map.values().toString());
return matcher.matches();
}
Why are you searching the values of a hashtable in the first place? That's guaranteed to be at least O(N) (and stringifying the whole thing, as you suggested elsewhere, is even worse since it also requires O(N) extra allocations and aa large constant factor as well). What you want is to build an inverted index which maps backward (from strings to integers). Most likely you want to use a trie, which will give you O(M) (where M is the maximum string length) lookup performance.