I am trying to figure out how to go about searching some user input for multiple keywords.The keywords come from a hash map called Synonym. So basically I enter some sentence and if the sentence contains one or more keywords or keyword synonyms I want to call a parse file method. So far I could only search for one keyword. I am stuck trying to get a user input which could be a long sentence or just one word containing the keyword(s) and search the hash map key for that matching word. For example, If the hash map is
responses.put("textbook name", new String[] { "name of textbook", "text", "portfolio" });
responses.put("current assignment", new String[] { "homework","current work" });
and the user inputs " what is the name of textbook that has the homework" I want to search a text file for textbook current assignment. Assuming that the text file contains the sentence The current assignment is in the second textbook name ralphy". I mean i got most of my implementation done, the issue is dealing with more than one keyword. Can someone help me solve this?
Here is my code
private static HashMap<String, String[]> responses = new HashMap<String, String[]>(); // this
public static void parseFile(String s) throws FileNotFoundException {
File file = new File("data.txt");
Scanner scanner = new Scanner(file);
while (scanner.hasNextLine()) {
final String lineFromFile = scanner.nextLine();
if (lineFromFile.contains(s)) {
// a match!
System.out.println(lineFromFile);
// break;
}
}
}
private static HashMap<String, String[]> populateSynonymMap() {
responses.put("test", new String[] { "test load", "quantity of test","amount of test" });
responses.put("textbook name", new String[] { "name of textbook", "text", "portfolio" });
responses.put("professor office", new String[] { "room", "post", "place" });
responses.put("day", new String[] { "time", "date" });
responses.put("current assignment", new String[] { "homework","current work" });
return responses;
}
public static void main(String args[]) throws ParseException, IOException {
/* Initialization */
HashMap<String, String[]> synonymMap = new HashMap<String, String[]>();
synonymMap = populateSynonymMap(); // populate the map
Scanner scanner = new Scanner(System.in);
String input = null;
/*End Initialization*/
System.out.println("Welcome To DataBase ");
System.out.println("What would you like to know?");
System.out.print("> ");
input = scanner.nextLine().toLowerCase();
String[] inputs = input.split(" ");
for (String ing : inputs) { // iterate over each word of the sentence.
boolean found = false;
for (Map.Entry<String, String[]> entry : synonymMap.entrySet()) {
String key = entry.getKey();
String[] value = entry.getValue();
if (input.contains(key) || key.contains(input)|| Arrays.asList(value).contains(input)) {
found = true;
parseFile(entry.getKey());
}
}
}
}
Any help would be appreciated
I have answered very similar question Understand two or more keys with Hashmaps. But I'll make my point more clear. In the current set of datastructures that you have used lets consider the following structures
1) Input List --> Spilt words in the sentence (may be in order) and keep it in a list example [what,is,the,name,of,textbook,that,has,the,homework]
2) Keyword list --> All keys from the Hashmap database you are using example [test,textbook name,professor office]
Now you have to set some criteria by which you say I can have max 3 words phrase out of sentence (example 'name of textbook')as keyword, why this criteria - to limit the processing, otherwise you'll end up checking lot of combinations of input.
Once you have this, you check whats common in input list and keyword list for criteria you have set. If you don't set criteria then you may try all the combinations against the key set.Once you find single or multiple match, output the synonym list etc.
Example check [name of textbook] against all your keys of the map.
If you want to reverse check, the do the same process by creating a list of synonyms and checking it.
My two tips tackling this problem
1) Define set of keywords and don't check with value list, Hash map structure is not good for that. In this be prepared for redundant data.
2) Set how many words in order you want to search in this keyset. And preferably only keep distinct words.
Hope this helps!
You could use a single regex pattern per "dictionary entry" and test each pattern against your input. Depending on your performance requirements and the size of your dictionary and input, it might be a good solution.
If you're using java 8, try this:
public static class DicEntry {
String key;
String[] syns;
Pattern pattern;
public DicEntry(String key, String... syns) {
this.key = key;
this.syns = syns;
pattern = Pattern.compile(".*(?:" + Stream.concat(Stream.of(key), Stream.of(syns))
.map(x -> "\\b" + Pattern.quote(x) + "\\b")
.collect(Collectors.joining("|")) + ").*");
}
}
public static void main(String args[]) throws ParseException, IOException {
// Initialization
List<DicEntry> synonymMap = populateSynonymMap();
Scanner scanner = new Scanner(System.in);
// End Initialization
System.out.println("Welcome To DataBase ");
System.out.println("What would you like to know?");
System.out.print("> ");
String input = scanner.nextLine().toLowerCase();
boolean found;
for (DicEntry entry : synonymMap) {
if (entry.pattern.matcher(input).matches()) {
found = true;
System.out.println(entry.key);
parseFile(entry.key);
}
}
}
private static List<DicEntry> populateSynonymMap() {
List<DicEntry> responses = new ArrayList<>();
responses.add(new DicEntry("test", "test load", "quantity of test", "amount of test"));
responses.add(new DicEntry("textbook name", "name of textbook", "text", "portfolio"));
responses.add(new DicEntry("professor office", "room", "post", "place"));
responses.add(new DicEntry("day", "time", "date"));
responses.add(new DicEntry("current assignment", "homework", "current work"));
return responses;
}
Sample output:
Welcome To DataBase
What would you like to know?
> what is the name of textbook that has the homework
textbook name
current assignment
Make a list/append the keys that match. As for the given example , when keyword "textbook" matches store it in a "temp" variable. Now, continue the loop, now keyword "current" matches , append this to variable temp. So, now temp contains "textbook current". Similairly, continue and append the next keyword "assignment" into "temp".
Now, temp contains "textbook current assignment".
Now at the end call the parseFile(temp).
This should work for single or multiple matches.
//Only limitation is the keys are to be given in a ordered sequence , if you want
// to evaluate all the possible combinations then better add all the keys in a list
// And append them in the required combination.
//There might be corner cases which I havent thought of but this might help/point to a more better solution
String temp = "";
//flag - used to indicate whether any word was found in the dictionary or not?
int flag = 0;
for (String ing : inputs) { // iterate over each word of the sentence.
boolean found = false;
for (Map.Entry<String, String[]> entry : synonymMap.entrySet()) {
String key = entry.getKey();
String[] value = entry.getValue();
if (input.contains(key)) {
flag = 1;
found = true;
temp = temp +" "+ key;
}
else if (key.contains(input)) {
flag = 1;
found = true;
temp = temp +" "+ input;
}
else if (Arrays.asList(value).contains(input)) {
flag = 1;
found = true;
temp = temp +" "+ input;
}
}
}
if (flag == 1){
parseFile(temp);
}
Related
I am writing a programme that reads a word String from a file. I am translating the string but the translator returns a Set
What I want to be able to do is to store the original word and the translated word next to each other in an ArrayList.
while (sc.hasNext()) {
String entry = (sc.next());
System.out.println(word);
al.add(ord);//String;
al.add(translate(word));//Set<String>;
}
Now what I want to do is to access both the word and the translated word....just to test I am trying to print but this is where my code is broken.....
for(int i=0;i<al.size();i++){
Object o = al.get(i);
Object p = al.get(i);
System.out.println("Value is "+o.toString());
System.out.println("Value is "+p.toSet<?>());
}
I would suggest not using a list. If you want to know which word was translated into which Set, use a Map (or a dictionary in other languages}
Map<String, Set<String>> translations = new HashMap<>();
while (sc.hasNext()) {
String entry = sc.next();
translations.put(entry, translate(entry));
}
System.out.println(translations);
The problem
System.out.println("Value is "+p.toSet<?>());
This won't compile. There is no toSet<>() function for java.lang.Object.
Another solution
The best solution for this might be to store the original word in a Map with the translated word as the value and the original word as the key. This would only work if there are no conflicts (i.e duplicate strings).
Map<String, Set<String>> words = new HashMap<>();
while (scanner.hasNext()) {
String word = sc.next();
Set<String> translated = translate(word);
words.put(word, translated);
}
I am currently working on an assignment for school where I am supposed to create a hashmap within a hashmap like this:
Map<String, Map<String, Integer>> girlsByYear = new HashMap<>();
Map<String, Map<String, Integer>> boysByYear = new HashMap<>();
According to the professor, there are not supposed to be any other maps needed to complete the assignment. The maps add elements to them by accessing files within a package that contain names, gender, and rank of baby names by year. When the program is compiled and finished, a JavaFX chart is created that requests a girl or boy's name to be entered. When entered, the chart shows by year the rank of how popular the name was.
Currently, I have figured most of it out, but I cannot understand how to access the Hashmap within the first Hashmap without a key for the first hashmap. By that, I mean that I am supposed to have a check performed by the textbox for the JavaFX class that reviews whether the name is in the Hashmap or not. Here is my code:
public class NameHelper {
// Declare the hash maps.
Map<String, Map<String, Integer>> girlsByYear = new HashMap<>();
Map<String, Map<String, Integer>> boysByYear = new HashMap<>();
// Declare addition variables.
String firstWord = "";
String secondWord = "";
String thirdWord = "";
Integer rank;
String fileName;
// This method will load the files from the data package, review the files,
// and add each item respectively to either map.
public void load() throws FileNotFoundException {
File dir = new File("src/data");
File [] files = dir.listFiles();
// for each file in the directory...
for (File f : files)
{
// Get the file name and split the year from it to add to each name.
String newFileName = f.getName();
fileName = newFileName.replaceAll("[yobtxt.]","");
Scanner scanner = new Scanner(f);
// While the files are not empty.
while(scanner.hasNextLine()) {
// If the second column split by a delimiter is M then add the information
// to the boys. Else girls.
String input = scanner.nextLine();
// Set the input to string values to enter into each hash map.
String initial = input.split(",")[1];
firstWord = fileName;
secondWord = (input.split(",")[0]).toLowerCase();
thirdWord = input.split(",")[2];
rank = Integer.parseInt(thirdWord);
// Use a switch statements since if statements aren't working.
switch(initial) {
case "M":
boysByYear.put(firstWord, new HashMap<String, Integer>());
boysByYear.get(firstWord).put(secondWord, rank);
break;
case "F":
girlsByYear.put(firstWord, new HashMap<String, Integer>());
girlsByYear.get(firstWord).put(secondWord, rank);
break;
default:
System.out.println("This is an issue");
break;
}
}
// Close the scanner.
scanner.close();
}
}
// This method will return a sorted set of years by getting the keyset from the hashmaps.
public Set<String> getYears() {
// Create the set.
Set<String> set = new HashSet<>();
// Add all the years of the listed by file name.
for(String key : girlsByYear.keySet()) {
set.add(key);
}
// Convert the set to a sorted set.
TreeSet<String> treeSet = new TreeSet<>(set);
return treeSet;
}
// This method will return true if the supplied name is found in the data structure.
// Use the gender input to determine which map to search by using "containsKey".
public boolean isNamePresent(String name, String gender) {
if(gender == "M") {
//Check if the name is within the map's map.
if(boysByYear.get(name).containsKey(name)) {
return true;
}
}
else if(gender == "F") {
if(girlsByYear.containsKey(name.toLowerCase())) {
return true;
}
}
return false;
}
The section that I need help with is the isNamePresent method. I need to check if the name is in the key of the second hashmap which is set up in this format (String year, HashMap(String name, Integer rank))
Any help or guidance would be greatly appreciated!
Additional notes: The JavaFx section for the chart was provided by the professor.
One thing you need to fix first is comparing the strings using ==. This doesn't work unless both the string passed as gender parameter is a string literal. You need to use equals instead, see How do I compare strings in Java? (switch does this automatically).
Furthermore you should avoid duplicating code by retrieving the map to a local variable:
Map<String, Map<String, Integer>> map;
switch (gender) {
case "M":
map = boysByYear;
break;
case "F":
map = girlsByYear;
break;
default:
return false; // alternatively throw new IllegalArgumentException();
}
To find out, if at least one of the maps contains name as a key, go through all the values and check the maps:
final String nameLower = name.toLowerCase();
return map.values().stream().anyMatch(m -> m.containsKey(nameLower));
BTW: You need to fix the way you read the data. Otherwise you'll get at most one name per year&gender, since you replace the Map. Furthermore I recommend storing the result of split instead of invoking it 3 times. Also don't use fields as variables only needed in a loop and choose more discriptive variable names:
Map<String, Integer> boys = new HashMap<>();
Map<String, Integer> girls = new HashMap<>();
boysByYear.put(fileName, boys);
girlsByYear.put(fileName, girls);
while(scanner.hasNextLine()) {
// If the second column split by a delimiter is M then add the information
// to the boys. Else girls.
String input = scanner.nextLine();
String[] parts = input.split(",");
// Set the input to string values to enter into each hash map.
String gender = parts[1];
String name = parts[0].toLowerCase();
int rank = Integer.parseInt(parts[2]);
switch(gender) {
case "M":
boys.put(name, rank);
break;
case "F":
girls.put(name, rank);
break;
default:
System.out.println("This is an issue");
break;
}
}
To access the inner hash map without knowing the key of the outer map, you can iterate over each entry of the outer map.
for(Map.Entry<String, Integer> mapEntry: boysByYear.entrySet()){
// Get the innerMap and check if the name exists
Map<String, Integer> innerMap = mapEntry.getValue();
if(innerMap.containsKey(name)){
return true;
}
}
I am using a HashMap to store the full forms for abbreviations.
public class Test {
public static void main(String[] args) {
Map<String, String> slangs = new HashMap<String, String>();
slangs.put("lol", "laugh out loud");
slangs.put("r", " are ");
slangs.put("n", " and ");
slangs.put("idk", " I don't know ");
slangs.put("u", " you ");
Set set = slangs.entrySet();
Iterator i = set.iterator();
String sentence = "lol how are you";
StringBuilder sb = new StringBuilder();
for (String word : sentence.split(" ")) {
while(i.hasNext()) {
Map.Entry<String, String> me = (Map.Entry)i.next();
if (word.equalsIgnoreCase(me.getKey())) {
sb.append(me.getValue());
continue;
}
sb.append(word);
}
}
System.out.println(sb.toString());
}
}
The Output is:
lollollollaugh out loudlol
What is wrong here and how do I solve it?
You are not supposed to iterate over the entries to find a match, you are supposed to use get(Object key) or getOrDefault(Object key, V defaultValue) to get the full form of a given abbreviation, otherwise instead of getting your full form with a time complexity of O(1), you will get it with a O(n) which is of course not good in term of performances, you would lose the real benefit of having your key/value pairs in a Map. If you did it because of the case, simply put your keys only in lower case in your map and call get or getOrDefault with the word in lower case as below:
So your loop should be something like:
for (String word : sentence.split(" ")) {
// Get the full form of the value of word in lower case otherwise use
// the word itself
sb.append(slangs.getOrDefault(word.toLowerCase(), String.format(" %s", word)));
}
Output:
laugh out loud how are you
Using the Stream API, it could simply be:
String result = Pattern.compile(" ")
.splitAsStream(sentence)
.map(word -> slangs.getOrDefault(word.toLowerCase(), word))
.collect(Collectors.joining(" "));
Don't loop over the keys in the dictionary. Instead, just check whether the key is in the map and get the corresponding value. Also, don't forget to add the spaces back into the combined sentence.
for (String word : sentence.split(" ")) {
if (slangs.containsKey(word.toLowerCase())) {
sb.append(slangs.get(word.toLowerCase()));
} else {
sb.append(word);
}
sb.append(" ");
}
If you are using Java 8, you can also use String.join, Map.getOrDefault and Streams:
String s = String.join(" ", Stream.of(sentence.split(" "))
.map(word -> slangs.getOrDefault(word.toLowerCase(), word))
.toArray(n -> new String[n]));
This latter approach also has the benefit of not adding a space before the first or after the last word in the sentence.
Simply, I think you just need to check if slangs contain this keyword or not.
Please check my code.
public class Test {
public static void main(String[] args) {
Map<String, String> slangs = new HashMap<String, String>();
slangs.put("lol", "laugh out loud");
slangs.put("r", " are ");
slangs.put("n", " and ");
slangs.put("idk", " I don't know ");
slangs.put("u", " you ");
String sentence = "lol how are you";
String[] words = sentence.split(" ");
for (String word : words) {
String normalizeWord = word.trim().toLowerCase();
if(slangs.containsKey(normalizeWord)) {
sentence = sentence.replace(word, slangs.get(normalizeWord));
}
}
System.out.println(sentence);
}
}
I want to make an user database, which creates a new variable from the string that was entered in the console. I don't know if this is possible and i've searched everywhere.
You can use data structures like a List, which can hold a number of objects. A list grows when you add objects to them. A simple list to get started with is java.util.ArrayList.
Example to get you started:
// create a new list which can hold String objects
List<String> names = new ArrayList<>();
String nextName = scanner.nextLine();
// read names until the user types stop
while(!nextName.equals("stop")) {
// add new name to the list. note: the list grows automatically.
names.add(nextName);
// read next name from user input
nextName = scanner.nextLine();
}
// print out all names.
for(String name : names) {
System.out.println(name);
}
Sure, like this:
// 1. Create a Scanner using the InputStream available.
Scanner scanner = new Scanner( System.in );
// 2. Don't forget to prompt the user
System.out.print( "Type some data for the program: " );
// 3. Use the Scanner to read a line of text from the user.
String input = scanner.nextLine();
// 4. Now, you can do anything with the input string that you need to.
// Like, output it to the user.
System.out.println( "input = " + input );
There are ways to do what you asked using Reflection. But that leads to a lot of problems and design issues.
Instead, try using a Key/Value store of some sort, and a simple class like this:
public class KeyValueField
{
public final String Key;
public final String Value;
public KeyValueField(String key, String value)
{
Key = key;
Value = value;
}
}
Usage like this:
System.out.print("Enter field name:");
String key= System.console().readLine();
System.out.print("Enter field value:");
String value = System.console().readLine();
KeyValueField newField = new KeyValueField(key, value);
I recomend using a Hashmap.
import java.util.HashMap;
HashMap<String, String> keyValue = new HashMap<String, String>();
keyValue.put(key, value);
I am new to Java. I want to Parse the data which is in this Format
Apple;Mango;Orange:1234;Orange:1244;...;
There could be more than one "Orange" at any point of time. Numbers (1,2...) increase and accordingly as the "Orange".
Okay. After splitting it, Lets assume I have stored the first two data(Apple, Orange) in a variable(in setter) to return the same in the getter function. And now I want to add the value(1234,1244....etc) in the 'orange' thing into a variable to return it later. Before that i have to check how many oranges have come. For that, i know i have to use for loop. But don't know how to store the "Value" into a variable.
Please Help me guys.
String input = "Apple;Mango;Orange:1234;Orange:1244;...;"
String values[] = input.split(";");
String value1 = values[0];
String value2 = values[1];
Hashmap< String, ArrayList<String> > map = new HashMap<String, ArrayList<String>>();
for(int i = 2; i < values.length; i = i + 2){
String key = values[i];
String id = values[i+1];
if (map.get(key) == null){
map.put(key, new ArrayList<String>());
}
map.get(key).add(id);
}
//for any key s:
// get the values of s
map.get(s); // returns a list of all values added
// get the count of s
map.get(s).size(); // return the total number of values.
Let me try to rephrase the question by how I interpreted it and -- more importantly -- how it focuses on the input and output (expectations), not the actual implementation:
I need to parse the string
"Apple;Mango;Orange:1234;Orange:1244;...;"
in a way so I can retrieve the values associated (numbers after ':') with the fruits:
I should receive an empty list for both the Apple and Mango in the example, because they have no value;
I should receive a list of 1234, 1244 for Orange.
Of course your intuition of HashMap is right on the spot, but someone may always present a better solution if you don't get too involved with the specifics.
There are a few white spots left:
Should the fruits without values have a default value given?
Should the fruits without values be in the map at all?
How input errors should be handled?
How duplicate values should be handled?
Given this context, we can start writing code:
import java.util.*;
public class FruitMarker {
public static void main(String[] args) {
String input = "Apple;Mango;Orange:1234;Orange:1244";
// replace with parameter processing from 'args'
// avoid direct implementations in variable definitions
// also observe the naming referring to the function of the variable
Map<String, Collection<Integer>> fruitIds = new HashMap<String, Collection<Integer>>();
// iterate through items by splitting
for (String item : input.split(";")) {
String[] fruitAndId = item.split(":"); // this will return the same item in an array, if separator is not found
String fruitName = fruitAndId[0];
boolean hasValue = fruitAndId.length > 1;
Collection<Integer> values = fruitIds.get(fruitName);
// if we are accessing the key for the first time, we have to set its value
if (values == null) {
values = new ArrayList<Integer>(); // here I can use concrete implementation
fruitIds.put(fruitName, values); // be sure to put it back in the map
}
if (hasValue) {
int fruitValue = Integer.parseInt(fruitAndId[1]);
values.add(fruitValue);
}
}
// display the entries in table iteratively
for (Map.Entry<String, Collection<Integer>> entry : fruitIds.entrySet()) {
System.out.println(entry.getKey() + " => " + entry.getValue());
}
}
}
If you execute this code, you will get the following output:
Mango => []
Apple => []
Orange => [1234, 1244]