I am extremely unsure of how to do this. I understand (I believe) that you use
String[] user {"stuff","other stuff","more stuff"};
I'm developing a chat bot and I need it to be able to recognize what the user said and if it's inside of the array (it's "database," per say), then it will respond accordingly.
Something simple so that I could be able to say, "How are you?" And it would look for "How are you?" or something at least close to it and respond accordingly with a random positive word. I've achieved this function by simply using lots of if-else statements, but this is far too much coding.
If I understand you correctly, you want your bot to respond to some prompt from the user. In that case, you could use a Map<String, String> to store query-answer pairs.
Map<String, String> answers = new HashMap<String, String>();
answers.put("How are you?", "Great!");
answers.put("Where is the cake?", "The cake is a lie");
and then just check whether the query string is in answers:
public String answerUser(String query) {
if (answers.containsKey(query)) {
return answers.get(query);
} else {
return "I don't understand.";
}
}
If you want more than one possible answer, use a Map<String, List<String>> and select randomly from the list:
public String answerUser(String query) {
Random rand = new Random();
if (answers.containsKey(query)) {
List<String> ans = answers.get(query);
int id = rand.nextInt(ans.size());
return ans.get(id);
} else {
return "I don't understand.";
}
}
This is a very big and complicated subject, and you haven't really posted enough to go on.
To split a string into words use String#split(), that will give you the array you want. (you probably want to split on all non-alpha, or all whitespace).
You then want to define keywords that your AI responds to, and scan the array for any of those keywords.
Use some sort of scoring system to then determine the appropriate response.
For example you could have a HashMap of String to a class that has both a weight and a meaning. Go through the sentence, sum up the scores for every action you find. Take appropriate decisions based on the combined values.
That's a very simple algorithm and much better ones are possible but they are a lot harder and this will get you started.
You could use a list instead of array, which will give you the contains() method. For instance:
List<String> words = new ArrayList<String>();
words.add("Hello");
words.add("bye");
if(words.contains("hello")) {
//do something
}
Another option would be to map the phrase to the response:
Map<String, String> wordMap = new HashMap<String, String>();
wordMap.put("How are you?", "Great");
wordMap.put("Where are you?", "Work");
wordMap.get("How are you?");
Combine these and map a phrase to a list of responses if you want.
An intelligent chat bot would need a lot more design, but if you just wanted a solution where "I say this, then you say that" than you could do it a couple ways.
Using Two Arrays
Since you know how to use one array, Figured I'd start here for simplicity.
String[] userInput {"how are you", "how are you?", "how you doing?"};
String[] response {"good", "terrible", "is that a joke?"};
//Go through each userInput string and see if it matches what you typed in.
//If it matches, print the corresponding position in the response array.
Using a Map
Same idea, but is a collection that is more suited for the situation.
Map<String, String> response = new HashMap<String, String>();
response.add("how are you", "good");
//When you receive input, check the response map using the input as the key.
//Return the value as the response.
//Better documented in sebii's answer.
Related
so i'm working on a project and i need the following to work:
Let's say I have a String[] contatining out of f.e 3 values "0D", "0A", "01A0"
Now in the background I got like a defined description for each of these values and I want to show them in another string.
So in the end i want to call a method with String"0D" and the method returns me the description, in this example "speed"
same for the others, if i call the method with "0A" it returns String "Fuel Pressure"
Is there an efficient way for achieving this? Cause i've got a pretty long list and don't want to manually input all the descriptions to the commands..
Yeah a HashMap would work.
You could try this:
HashMap<String, String> valueDescription = new HashMap<>();
valueDescription.put("0D", "speed");
valueDescription.put("0A", "Fuel Pressure");
valueDescription.put("01A0", "Temperature");
public String getDescription(String value) {
if (valueDescription.containsKey(value)) {
return valueDescription.get(value);
} else {
return "Description not found";
}
}
I would consider using a hashmap of <String, String>. The key would be the command, and the value is the description.
So I'm trying retrieve specific substrings in values in a Hashmap constructed like this..
HashMap<ID, "Home > Recipe > Main Dish > Chicken > Chicken Breasts">
Which is passed from a different method that returns a HashMap
In above example, I need to retrieve Chicken.
Thus far, I have..
public static ArrayList<String> generalize() {
HashMap<String, String> items = new HashMap<>();
ArrayList<String> cats = new ArrayList<>();
items = RecSys.readInItemProfile("PATH", 0, 1);
for(String w : items.values()) {
cats.add(w);
}
for(String w : cats) {
int e = w.indexOf('>', 1 + w.indexOf('>', 1 + w.indexOf('>')));
String k = w.substring(e+1);
System.out.print(k);
e = 0;
}
System.out.println("k" + cats);
return cats;
}
Where I try to nullify String e for each iteration (I know it's redundant but it was just to test).
In my dataset, the first k-v pair is
3880=Home > Recipes > Main Dish > Pasta,
My output is
Pasta
Which is ok. If there are more than 3x ">", it'll return all following categories. Optimally it wouldn't do that, but it's ok if it does. However, further down the line, it (seemingly) randomly returns
Home > Recipe
Along with the rest of the data...
This happens at the 6th loop, I believe.
Any help is greatly appreciated..
Edit:
To clarify, I have a .csv file containing 3 columns, whereas 2 are used in this function (ID and Category). These are passed to this function by a read method in another class.
What I need to do is extract a generalized description of each category, which in all cases is the third instance of category specification (that is, always between the third and fourth ">" in every k-v pair).
My idea was to simply put all values in an arraylist, and for every value extract a string from between the third and fourth ">".
I recommend using the following map:
Map<Integer, List> map = new HashMap<>();
String[] vals = new String[] { "HomeRecipe", "Main Dish", "Chicken",
"Chicken Breasts" };
map.put(1, Arrays.asList(vals));
Then, if you need to find a given value in your original string using an ID, you can simply call ArrayList#get() at a certain position. If you don't care at all about order, then a map of integers to sets might make more sense here.
If you can. change your data structure to a HashMap<Integer, List<String>> or HashMap<Integer, String[]>. It's better to store the categories (by cats you mean categories right?) in a collection instead of a string.
Then you can easily get the third item.
If this is not possible. You need to do some debugging. Start by printing every input and output pair and find out which input caused the unexpected output. Your indexOf method seems to work at first glance.
Alternatively, try this regex method:
String k = cats.replaceAll("(?:[^>]+\\s*>\\s*){3}([^>]+).*", "$1");
System.out.println(k);
The regex basically looks for a xxx > yyy > zzz > aaa ... pattern and replaces that pattern with aaa (whatever that is in the original string).
Ive been searching SO about this question and most only have the problem with two arrays comparing by have a nested loop. My problem is quite the same but on a bigger scale. Suppose I have a 100 or thousand user on my app, and each user has the list of item it wants.
Something like this
User1 = {apple,orange,guava,melon,durian}
User2 = {apple, melon,banana,lemon,mango}
User3 = {orange,carrots,guava,melon,tomato}
User4 = {mango,carrots,tomato,apple,durian}
.
.
Nuser = ...
I wanted to see how many apples or oranges was listed from all the users array. So I am basically comparing but on a bigger scale. The data isn't static as well, A user can input an unknown fruit from the developers knowledge but on the users knowledge they can put it there so there can be multiple users that can put this unknown fruit and yet the system can still figure out how many is this unknown item was listed. Keep in mind this is a dynamic one. User can reach for example a 100 users depending how popular an app would be. I can't afford to do nested loop here.
PS this is not the exact problem but it is the simplest scenario I can think of to explain my problem.
PS: just to clarify, I dont intend to use 3rd party lib as well like guava. I am having a problem on proguard with it.
Edit
Just read that Original poster cannot use Java 8, which is a pity, because this would realy make it very easy!
Java 7 solution
final Map<String, Integer> occurencesByFruit = new HashMap<>();
for (User user : users) {
String[] fruits = user.getFruits();
for (String fruit : fruits) {
final Integer currentCount = occurencesByFruit.get(fruit);
if (currentCount == null) {
occurencesByFruit.put(fruit, 1);
} else {
occurencesByFruit.put(fruit, currentCount + 1);
}
}
}
Java 8 solution
I'd stream the users, flatMap() to the actual fruit elements, and then use Collectors.groupingBy() with a downstream collector Collectors.counting().
This will give you a Map where the keys are the fruits, and the values are the occurrences of each fruit throughout all your users.
List<User> users = Arrays.asList(/* ... */);
final Map<String, Long> occurencesByFruit = users.stream()
.map(User::getFruits)
.flatMap(Arrays::stream)
.collect(Collectors.groupingBy(f -> f, Collectors.counting()));
Seems it is a good possibility to use HashMap<Item, Integer> fruits. You could iterate over all Users (you would need to store all Users in some kind of list, such as ArrayList<User> users) and check the list of items chosen by each User (I suppose User should have a field ArrayList<Item> items in its body to store items). You could achieve it with something like that:
for (User user : users) { // for each User from users list
for (Item item : user.items) { // check each item chosen by this user
if (fruits.containsKey(item) { // if the fruit is already present in the items HashMap increment the amount of items
int previousNumberOfItems = fruits.get(item);
fruits.put(item, ++previousNumberOfItems);
else { // otherwise put the first occurrency of this item
fruits.put(item, 1);
}
}
}
I would either create an ArrayList containing a HashMap with strings and ints or use two ArrayLists (one of type String and one of type Integer). Then you can iterate over every entry in each of the user arrays (this is only a simple nested loop). For every entry in the current user array you check if there is already the same entry in the ArrayList you created additionally. If so, you increment the respective int. If not, you add a string and an int. In the end, you have the number of occurrences of all the fruit strings in the added ArrayLists, which is, if I understood you correctly, just what you wanted.
Sorry about the non-descriptive title, I couldn't think of a way to explain it better short of 100 words. What I would like to be able to do is sort a list of strings into "boxes" based on a string associated with the main string and an array of strings "order".
For my current setup I am using a HashMap to store the string and it's associated "place-in-the-order" string.
I am aware that my explanation is truly crap so I have made an image which I hope will explain it better:
The variables are initialised as follows:
private final String[] order = new String[] {"Severe", "Warning", "Info"};
private final Box[] boxes = new Box[] {new Box(1), new Box(2), new Box(3), new Box(4)};
private final Map<String, String> texts = new HashMap<String, String>();
texts.put("Server on fire!", "Severe");
texts.put("All is good!", "Info");
texts.put("Monkeys detected!", "Warning");
texts.put("Nuke activated!", "Severe");
This shouldn't be too hard to implement but the only way I can think of doing it is by using 3 loops which seems a bit wasteful and would be slow if there was large numbers of any of the inputs.
Here is some example code which will hopefully show what I have come up with so far and perhaps explain the problem, I have not tested it and don't have an IDE handy so have probably overlooked something.
Set<Box> usedBoxes = new HashSet<Box>();
for(String curOrder : order) {
for (String text : texts) {
if (texts.get(text).equals(order)) {
for (Box box : boxes) {
if (!usedBoxes.contains(box)) {
box.setText(text);
usedBoxes.add(box);
break;
}
}
}
}
}
I'm not sure I fully understand what you want to achieve, but I feel that there are two things that would make your design much simpler:
Don't use Strings for your severity levels. Use enums instead. Enums have a name, may have other fields and methods, and are naturally ordered using their order of definition. And there is no way to make a typo and introduce an unknown severity: they're type-safe
enum Severity {
SEVERE, WARNING, INFO
}
Don't store things in parallel arrays or associate them with maps. Define a class containing the information of your objects:
public class Box {
private String text;
private Severity severity;
}
Now that you have these, you can simply create a List<Box>, and sort it using a Comparator<Box> which sorts them by severity, for example:
List<Box> boxes = Arrays.asList(new Box("Server is on fire", Severity.SEVERE),
new Box("All is good", Severity.INFO),
...);
Collections.sort(boxes, new Comparator<Box>() {
#Override
public int compare(Box b1, Box b2) {
return b1.getSeverity().compareTo(b2.getSeverity());
}
}
or even simpler, with Java 8:
boxes.sort(Comparator.comparing(Box::getSeverity));
You should make your "statuses" (Severe, Info etc) into an Enum.
public enum StatusLevel {
Severe,
Warning,
Info;
}
You can then sort by StatusLevel natively as long as you define the in a top to bottom order.
If you want to supply your Box object directly insead of pulling out the StatusLevel or have a secondary sort by another property like time or alphabetically you should implement your own Comparator
Also you may want to look into SortedMap or other Map that keeps its order so you don't have to resort a HashMap every time as it does not guarantee order.
I have a HashSet of Strings in the format: something_something_name="value"
Set<String> name= new HashSet<String>();
Farther down in my code I want to check if a String "name" is included in the HashSet. In this little example, if I'm checking to see if "name" is a substring of any of the values in the HashSet, I'd like it to return true.
I know that .contains() won't work since that works using .equals(). Any suggestions on the best way to handle this would be great.
With your existing data structure, the only way is to iterate over all entries checking each one in turn.
If that's not good enough, you'll need a different data structure.
You can build a map (name -> strings) as follows:
Map<String, List<String>> name_2_keys = new HashMap<>();
for (String name : names) {
String[] parts = key.split("_");
List<String> keys = name_2_keys.get(parts[2]);
if (keys == null) {
keys = new ArrayList<>();
}
keys.add(name);
name_2_keys.put(parts[2], keys);
}
Then retrieve all the strings containing the name name:
List<String> keys = name_2_keys.get(name)
You can keep another map where name is the key and something_something_name is the value.
Thus, you would be able to move from name -> something_something_name -> value. If you want a single interface, you can write a wrapper class around these two maps, exposing the functionality you want.
I posted a MapFilter class here a while ago.
You could use it like:
MapFilter<String> something = new MapFilter<String>(yourMap, "something_");
MapFilter<String> something_something = new MapFilter<String>(something, "something_");
You will need to make your container into a Map first.
This would only be worthwhile doing if you look for the substrings many times.