Best way to match similar words? - java

I am trying to display a list of words that are similar to what the user is typing. For example, if I have a list of words like ["Software Eng 1", "Software Eng 2", "Blah"]
And the user typed S, it would filter to the Software Eng 1 and Software Eng 2. Again, the user types So and it filters to the same two words. But if the user types Soc, it would have nothing. What is the best way to do this? I tried
for (EmployeeName r : list)
{
if (textField.getText().matches(r.getName()))
{
System.out.println(r.getName() + " is similar");
}
else System.out.println("NOPE");
}
But this only seems to be catching the case when textField.getText() is exactly the same

you could use String#startsWith if you're checking whether
any Employees name within the list starts with the text entered.
Example:
for (EmployeeName r : list)
{
if (r.getName().startsWith(textField.getText()))
{
System.out.println(r.getName() + " is similar");
}
}
if you're looking to see if the text entered is contained anywhere within the Employees name
then String#contains would do the job.
you could even use String#indexOf to check if the text entered is contained anywhere within the Employees name.
Example:
for (EmployeeName r : list)
{
if (r.getName().indexOf(textField.getText()) != -1)
{
System.out.println(r.getName() + " is similar");
}
}

You can use contains for example ;
textField.getText().toLowerCase().contains(r.getName().toLowerCase())

Related

HashMap: Match a string with a value if it contains part of the string?

I have a text file, which is filled with many lines of code in the layout: "fiesta, ford". I read these lines with new BufferedReader(new InputStreamReader(...)) and then I display them in my app.
This method findCar() takes an input, compares it with the keys of the hashmap which are taken from the text file, and if it matches, it displays the car model and the card brand in another layout part :). What is for example: Is there a possibility to make my code a little more advanced? Let's say that the user types Fiesta 2018, or Fiesta 2019. I want it to be accepted. So I want to say that if the given string contains "Fiesta", match it with "ford" and display them. I don't know how to make it work for every other brand, and i don't want to have 100 if statements.
I am sorry for the rookie question, I have tried many many versions like else if(car.equalsIgnoreCase(carItem.getKey())){ but I can't make it work.
public String findCar(String car) {
String x = "";
for (HashMap.Entry<String, String> carItem: itemsMap.entrySet()) {
if (carItem.getKey().equalsIgnoreCase(car)){
x = x + "Found " + carItem.getKey() + " in:" + carItem.getValue();
return x;
}
}
return "Sorry, not found :D";}
You should try following code (use "contains" function and convert to one case both strings) :
public String findCar(String car) {
String carLowerCase = car.toLowerCase().trim();
String x = "";
for (HashMap.Entry<String, String> carItem: itemsMap.entrySet()) {
String key = carItem.getKey().toLowerCase().trim();
if (carLowerCase.contains(key)){
x = x + "Found " + carItem.getKey() + " in:" + carItem.getValue();
return x;
}
}
return "Sorry, not found :D";
}
You want to refine your keyword.
For example, in the
Fiesta 2018
you only need the Fiesta part. So it makes sense say if the last four letters are digits, you simply remove it.
For example:
Map<String,String> modelBrand = new HashMap<>();
String keyword = "Fiesta 2018";
String[] keys = keyword.split(" [0-9]{4}");
if(keys.length > 0) {
keyword = keys[0];
}
// Print not found since nothing in the map.
System.out.println(modelBrand.getOrDefault(keyword.toLowerCase(), "Not found"));
modelBrand.put("fiesta", "ford");
// Print ford as it finds the fiesta keyword.
System.out.println(modelBrand.getOrDefault(keyword.toLowerCase(), "Not found"));
A better way to do it is utilize a search engine, such as the open source solr.

Extra java input validation for strings

I want to make this so that short inputs can still be detected, such as "Londo" and "Lon", but want to keep it small and use it without basically copying and pasting the code, any tips? thank you.
if (Menu.answer1.equals("London"))
{
if (location.equals("London")) {
System.out.print(location + " ");
System.out.print(date + " ");
System.out.print(degrees + "C ");
System.out.print(wind + "MPH ");
System.out.print(winddirection + " ");
System.out.print(weather + " ");
System.out.println("");
}
You can use startsWith()
String city = "London";
if (city.startsWith("Lon")) {
// do something
}
Also if you need to check some substring, you can use contains method:
Menu.answer1 = "London";
Menu.answer1.contains("ondo"); // true
If you want to check against a fixed set of alternatives, you may use a list of valid inputs using contains:
List<String> londonNames = Arrays.asList("London", "Londo", "Lon");
if (londonNames.contains(Menu.answer1)) {
...
}
You can use (case-insensitive) regex to do the same, e.g.:
(?)Lon[a-z]{0,3} where
(?) = case insensitivity
Lon = Initial 3 characters
[a-z]{0,3} = any number of alphabets between 0 and 3
Here's an example:
String regex = "(?)Lon[a-z]{0,3}";
System.out.println("London".matches(regex));
System.out.println("Lond".matches(regex));
System.out.println("Lon".matches(regex));
If the underlying problem is that the user can enter one of several names, and you want to allow abbreviations, then a fairly standard approach is to have a table of acceptable names.
Given the user input, loop through the table testing "does the table entry start with the string typed by the user?" (like one of the previous answers here). If yes, then you have a potential match.
Keep looking. If you get a second match then the user input was ambiguous and should be rejected.
As a bonus, you can collect all names that match, and then use them in an error message. ("Pick one of London, Lonfoo, Lonbar").
This approach has the advantage (compared to a long chain of if-then-else logic) of not requiring you to write more code when all you want to do is have more data.
It automatically allows the shortest unique abbreviation, and will adjust when a once-unique abbreviation is no longer unique because of newly-added names.

Reverse lookup of ArrayList of Array values

In my quest to continue my java education I'm trying to figure out if there is a native java method that quickly and efficiently allows a lookup of a string value in a ArrayList of Arrays.
Here is my code that shows what I'm trying to do:
public void exampleArrayListofArray () {
ArrayList<String []> al = new ArrayList<>();
al.add(new String[] {"AB","YZ"});
al.add(new String[] {"CD","WX"});
al.add(new String[] {"EF","UV"});
al.add(new String[] {"GH","ST"});
al.add(new String[] {"IJ","QR"});
al.add(new String[] {"KL","OP"});
displayArrayListofArray(al);
}
public void displayArrayListofArray(List<String []> al) {
for (String [] row : al)
for (int column = 0; column <= 1 ; column ++){
System.out.println("Value at Index Row " + al.indexOf(row) +
" Column " + column + " is " + (row)[column]);
}
String lookUpString = "YZ";
lookUpMethod(al, lookUpString);
lookUpString = "ST";
lookUpMethod(al, lookUpString);
lookUpString = "IJ";
lookUpMethod(al, lookUpString);
lookUpString = "AA";
lookUpMethod(al, lookUpString);
}
public void lookUpMethod(List<String []> al, String lookUpString) {
boolean isStringFound = false;
for (String[] row : al) {
for (int column = 0; column <= 1; column++) {
if (al.get(al.indexOf(row))[column] == lookUpString) {
System.out.println("Index of '" + lookUpString + "': " + al.indexOf(row) + column);
isStringFound = true;
}
}
}
if (!isStringFound) {
System.out.println("Search string '" + lookUpString + "' does not exist.");
}
}
Is this the most efficient way of searching my ArrayList for a given string?
Is there anything that I should be doing to make my code more efficient (besides not using an ArrayList)?
I know that perhaps to do what I'm trying to do here there could be more efficient ways of doing it than an ArrayList such as a HashMap but with my currently very limited java knowledge I'm making progress with ArrayList and would have to start from scratch using a HashMap. The very end goal of my code is to do the following:
Read an asset text file to load the ArrayList
Search the ArrayList for a user entered value
Do some calcs with the neighbouring values in the searched row
Allow the user to update the neighbouring values at the searched row
Allow the user to add a new row if the searched string is not found
Save any changes back to the asset text file in alphabetical order
Airfix
My answer is: don't worry.
I think you are looking at this from the wrong angle: if you find that the users of your application have a "performance" issue; and if you then do profiling, and then profiling shows that your current "search" code is the "culprit" (the single hot-spot that kills "end user perceived performance"); then you will have to bite the bullet and learn about using different data structures than ArrayLists.
(side note there: in reality, Set/HashSet isn't much "different"; learning how to use them ... isn't as big of a deal as it might sound).
But: if you answered any of the above "questions" with "no" (like: you do not have users that complain about bad performance) ... then there is no point in worrying about performance.
Long story short: either performance is really an issue - then you have to solve it. Otherwise: don't try to fix something that is not broken.
(as said: from a learning perspective, I would still encourage you to save your code; and start a new version that uses sets. There are plenty of tutorials out there that explain all the things you need to know).
But just to give you some direction: your main "performance" killer is (as you thought yourself) the inappropriate usage of data structures. There is no advantage in using an ArrayList to store arrays of strings that you want to search for. That adds "two layers"; each one requiring your code to iterate those "lists" in an sequential way. If you would use a single Set (like HashSet) instead; and add all your "search strings" to that set, your whole "lookup" for matches ... boils down to ask that set: "do you contain this value".

Writing ArrayList of objects to textfile using filter

(Disclaimer: I am not done writing the coding, so I have been using lousy names to
identify things.)
I have an ArrayList of PersonObjects called PersonIndex.
The parameters of the PersonObjects are as follows: Name, Age, Gender, Height.
What I am trying to do is process the list and depending on what the last name starts with, it gets written to a different text file.
Because it is in bad taste to try combining three write methods to one writer, I have three different ones which are then called into a single method.
That method is as follows:
public void fullWriteNames(){
writeNamesA2K();
writeNamesL2R();
writeNamesS2Z();
}
I know the general layout of the writer method which is as follows:
String stopper = "stop";
try{
personWriter = new FileWriter(fileName2, true);
personWriter.write(*stuff to write*);
personWriter.close();
}
catch(IOException ioException){
System.out.println("Error.");
}
The issue I am having is how to use an if statement by the .write line to filter the objects. I know I need to use .startsWith, but otherwise I am clueless.
Any help would be appreciated. If there is any coding I left out here which would be relevant, please let me know.
Since characters are really just numbers, you could do something like this:
public void fullWriteNames() {
char first = lastname.charAt(0);
if (first >= 'A' && first <= 'K')
writeNamesA2K();
else if (first >= 'L' && first <= 'R')
writeNamesL2R();
else if (first >= 'S' && first <= 'Z')
writeNamesS2Z();
}
I'm assuming here that the lastname variable is your PersonObject's last name.
You could use a regular expression to match a group
List<String> names = new ArrayList<>(25);
names.add("Apple");
names.add("apple");
names.add("bannas");
names.add("Kings");
names.add("Nov");
names.add("Nov");
names.add("Yak");
for (String name : names) {
if (name.matches("[A-Ka-k].*")) {
System.out.println("[A-K] " + name);
} else if (name.matches("^[L-Rl-r].*")) {
System.out.println("[L-R] " + name);
} else if (name.matches("^[S-Zs-z].*")) {
System.out.println("[S-Z] " + name);
}
}
This might allow you to set up a write method which could take a "filter" parameter and make the decisions what to do based on the filter
public void writeToFile(List<Person> listOfNames, File fileToWrite, String filter) {
...
}
Which you could call using something like...
writeToFile(listOfPeople, fileForGroupAToK, "[A-Ka-k].*");
Personally, though, I might be tempered to generate groups of lists based on the filter which might be a little faster then iterating over the entire list 3 times...but that's me ;)

Searching a String array to find portions of elements

I have a String array that has individuals names in it (example):
["John Smith", "Ramon Ruiz", "Bill Bradford", "Suzy Smith", "Brad Johnson"]
I would like to write a method that prompts a user to input (in form of String) a name OR portion of a name, and then lists all names that contain the string entered by the user, (I can fix the case issue easily).
ex:
Name: rad (meaning user enters "rad")
Output:
Bill Bradford
Brad Johnson
Does anyone have any ideas on this (one that also preserves white spaces)? If there already is a good example of this, feel free to link me. I was unable to find a good method in API.
I would use
for(String name : names) {
if(org.apache.commons.lang3.StringUtils.containsIgnoreCase(name, stringToLookFor)) {
// Do your thing
}
}
You can use .indexOf() it return -1 if it does not find a subString into a String.
for(String name : myArray)
{
if (name.indexOf("rad") != -1) {
// contains word
}
}

Categories