string manipulation : insert words at certain indexes in string, simultaneously - java

I have searched a lot but couldn't find anything that will allow me to insert words at certain indexes simultaneously. For example :
I have a string :
rock climbing is fun, I love rock climbing.
I have a hashmap for certain words which indicate their index in the string :
e.g. :
rock -> 0,29
climbing -> 5,34
fun -> 17
Now my question is :
I want to put [start] tag at the start of all these words and [end] tag at the end of them, in the string. I can't do this one by one since in that case once I insert [start] at index 0, then all the other indexes will be modified and I'll have to recalculate them.
Is there a way in which I can insert all of the tags at once or something? Can somebody suggest some other solution to this problem?
I can't use regular expressions(replaceall method), since sometimes I'll have a sentence like :
rocks are hard.
and hashmap will be :
rock -> 0
I am looking for faster solutions here.
edit :
for the sentence :
rocks are hard but frocks are beautiful.
rocks -> 0
Here I don't want to replace frocks with the tags.

This is not exactly doing what you want, but consider this as an alternative solution what tries to achieve your goal via different means.
I will suppose two different things, firstly, suppose there exists a List<String> words, which contains the words you want to replace.
Then code will be:
public String insertTags(final String input) {
for (String word : words) {
input.replace(word, "[start]" + word + "[end]");
}
return input;
}
Second case, closer to your example but not using the indices, suppose there exists a Map<String, List<Integer>>, which contains the words and the indices to replaces them at in a list representation.
Then code would be:
public String insertTags(final String input) {
for (Map.Entry<String, List<Integer>> entry : words.entrySet()) {
String word = entry.getKey();
input.replace(word, "[start]" + word + "[end]");
}
return input;
}
The latter is definately more complex and does not even use the indices, so preferably you should rewrite it to the former.
Hope this helps you without having to worry about the indices.

Related

Change part of code to functional paradigm

I have few lines of code, which I'm trying to convert to functional paradigm. The code is:
private String test(List<String> strings, String input) {
for (String s : strings) {
input = input.replace(s, ", ");
}
return input;
}
I need to make this one instruction chain. It must replace all strings from given list with coma IN given String input. I tried to do it with map method, but with no success. I'm aware I can do it if I appended input string into list at beginning and call map then, but the list is immutable, so I cannot do that.
I believe you can do this with a simple reduce:
strings.stream().reduce(input, (in, s) -> in.replace(s, ", "));
It takes the input, and replaces each occurence of the first string with ", ". Then it takes that result, and uses it as the input along with the next string, and repeats for every item in the list.
As Louis Wasserman points out, this approach cannot be used with parallelStream, so it won't work if you want parallelization.
The only think I can think of -- which is pretty awkward -- is
strings.stream()
.map(s -> (Function<String, String>) (x -> x.replace(s, ", ")))
.reduce(Function.identity(), Function::andThen)
.apply(input)
The following does pretty much the same thing.
private String test(List<String> strings, String input) {
return input.replaceAll(
strings
.stream()
.map(Pattern::quote)
.collect(Collectors.joining("|"))
, ", "
);
}
The main difference is that it first combines all the search strings into a single regex, and applies them all at once. Depending on size of your input strings, this may perform even better than your original use case.
If the list of strings is fixed, or changes only rarely, you can get some more speed from precompiling the joined pattern.

Change String in List<String> based on another String same List Java

I've got a List containing String like those :
device0001;sale;2013-01-01 00:00:00;30.45
device0001;sale;2013-01-02 00:00:00;41.02
device0001;sale;2013-01-03 00:00:00;30.45
...
device0001;saleCode;2013-01-01 00:00:00;10
device0001;saleCode;2013-01-02 00:00:00;55
device0001;saleCode;2013-01-03 00:00:00;55
Multiple Device, multiple CodeName and Date by Device. I'd like to map the Value of the saleCode to the sale CodeName.
Example of what I'd like in the end :
device0001;10;2013-01-01 00:00:00;30.45
device0001;55;2013-01-02 00:00:00;41.02
device0001;55;2013-01-03 00:00:00;30.45
The saleCode String may or may not be kept, it doesn't matter.
I've made it work with 2 for loop and ifs, but it was way too long to process.
I thought about building something like this :
Map<String(device), Map<DateTime, Map<String(element), String(value)>>>
forEach device
forEach datetime
element (Codename substring) and replace by element (Value substring)
I'm pretty sure there must be a better and/or elegant way to do this.
EDIT - Since it doesn't seem so clear why I'm trying to do, here is the code with for and if (which is way too slow) :
for (String line : lines) {
if (line.split(SEPARATOR)[4].equals("sale")) {
for (String codeLine : lines) {
if (codeLine.split(SEPARATOR)[5].equals(line.split(SEPARATOR)[5]) &&
codeLine.split(SEPARATOR)[1].equals(line.split(SEPARATOR)[1])&&
codeLine.split(SEPARATOR)[4].equals("saleCode")) {
line = line.replaceAll("sale", codeLine.split(SEPARATOR)[7]);
}
}
}
}
The index doesn't fit with my string's examples only because there are other non important fields, but index [1] is the device number, [5] the date. [4] is the type (sale, saleCode) and [7] the value.
EDIT #2
I've improved the speed like so :
MultiKeyMap<String, String> multiKeyMap = new MultiKeyMap<>();
for (String line : lines) {
if (line.split(SEPARATOR)[4].equals("saleCode")) {
String device = line.split(SEPARATOR)[1];
String date = line.split(SEPARATOR)[5];
String value = line.split(SEPARATOR)[7];
multiKeyMap.put(device, date, value);
}
}
for (int i = 0; i < lines.size(); i++) {
String code = lines.get(i).split(SEPARATOR)[4];
if (code.equals("sale")) {
String device = lines.get(i).split(SEPARATOR)[1];
String date = lines.get(i).split(SEPARATOR)[5];
String newline = lines.get(i).replaceAll("sale", multiKeyMap.get(device, date));
lines.set(i, newline);
}
}
I'll go for that for the moment, but always open for advices.
If I understand your question correctly you don't need to build any maps etc.
You have a list of strings with that format.
Just go over each string and use a regular expression to replace/update each string.
Update:
Your code is slow because you are processing the list over and over for each string.
Create a hashmap based on device id.
Go over the strings in lines one by one.
Check if the string exists on hashmap.
If it does not exist then then check which type of string it is and apply a proper regex for replacement. Add the string to the hashmap
If it does exist then update the string via a regex using the newly encountered string.
When you are done the hashmap will have the strings replaced.
Note: I am mentioning regexes because it seems you have a specific format and it might be written easily and efficiently that way. If you can't use regexes e.g you are not familiar with them follow the approach of parsing it character by character as you are doing. Still it will be better as you process the list once

Java - Changing multiple words in a string at once?

I'm trying to create a program that can abbreviate certain words in a string given by the user.
This is how I've laid it out so far:
Create a hashmap from a .txt file such as the following:
thanks,thx
your,yr
probably,prob
people,ppl
Take a string from the user
Split the string into words
Check the hashmap to see if that word exists as a key
Use hashmap.get() to return the key value
Replace the word with the key value returned
Return an updated string
It all works perfectly fine until I try to update the string:
public String shortenMessage( String inMessage ) {
String updatedstring = "";
String rawstring = inMessage;
String[] words = rawstring.replaceAll("[^a-zA-Z ]", "").toLowerCase().split("\\s+");
for (String word : words) {
System.out.println(word);
if (map.containsKey(word) == true) {
String x = map.get(word);
updatedstring = rawstring.replace(word, x);
}
}
System.out.println(updatedstring);
return updatedstring;
}
Input:
thanks, your, probably, people
Output:
thanks, your, probably, ppl
Does anyone know how I can update all the words in the string?
Thanks in advance
updatedstring = rawstring.replace(word, x);
This keeps replacing your updatedstring with the rawstring with a the single replacement.
You need to do something like
updatedstring = rawstring;
...
updatedString = updatedString.replace(word, x);
Edit:
That is the solution to the problem you are seeing but there are a few other problems with your code:
Your replacement won't work for things that you needed to lowercased or remove characters from. You create the words array that you iterate from altered version of your rawstring. Then you go back and try to replace the altered versions from your original rawstring where they don't exist. This will not find the words you think you are replacing.
If you are doing global replacements, you could just create a set of words instead of an array since once the word is replaced, it shouldn't come up again.
You might want to be replacing the words one at a time, because your global replacement could cause weird bugs where a word in the replacement map is a sub word of another replacement word. Instead of using String.replace, make an array/list of words, iterate the words and replace the element in the list if needed and join them. In java 8:
String.join(" ", elements);

Comparing parts of Arrays against each other?

I'm really really really not sure what is the best way to approach this. I've gotten as far as I can, but I basically want to scan a user response with an array of words and search for matches so that my AI can tell what mood someone is in based off the words they used. However, I've yet to find a clear or helpful answer. My code is pretty cluttered too because of how many different methods I've tried to use. I either need a way to compare sections of arrays to each other or portions of strings. I've found things for finding a part of an array. Like finding eggs in green eggs and ham, but I've found nothing that finds a section of an array in a section of another array.
public class MoodCompare extends Mood1 {
public static void MoodCompare(String inputMood){
int inputMoodLength = inputMood.length();
int HappyLength = Arrays.toString(Happy).length();
boolean itWorks = false;
String[] inputMoodArray = inputMood.split(" ");
if(Arrays.toString(Happy).contains(Arrays.toString(inputMoodArray)) == true)
System.out.println("Success!");
InputMood is the data the user has input that should have keywords lurking in them to their mood. Happy is an array of the class Mood1 that is being extended. This is only a small piece of the class, much less the program, but it should be all I need to make a valid comparison to complete the class.
If anyone can help me with this, you will save me hours of work. So THANK YOU!!!
Manipulating strings will be nicer when you do not use the relative primitive arrays, where you have to walk through yourself etcetera. A Dutch proverb says: not seeing the wood through the trees.
In this case it seems you check words of the input against a set of words for some mood.
Lets use java collections:
Turning an input string into a list of words:
String input = "...";
List<String> sentence = Arrays.asList(input.split("\\W+"));
sentence.remove("");
\\W+ is a sequence of one or more non-word characters. Mind "word" mean A-Za-z0-9_.
Now a mood would be a set of unique words:
Set<String> moodWords = new HashSet<>();
Collections.addAll(moodWords, "happy", "wow", "hurray", "great");
Evaluation could be:
int matches = 0;
for (String word : sentence) {
if (moodWords.contains(word)) {
++matches;
}
}
int percent = sentence.isEmpty() ? 0 : matches * 100 / sentence.size();
System.out.printf("Happiness: %d %%%n", percent);
In java 8 even compacter.
int matches = sentence.stream().filter(moodWords::contains).count();
Explanation:
The foreach-word-in-sentence takes every word. For every word it checks whether it is contained in moodWords, the set of all mood words.
The percentage is taken over the number of words in the sentence being moody. The boundary condition of an empty sentence is handled by the if-then-else expression ... ? ... : ... - an empty sentence given the arbitrary percentage 0%.
The printf format used %d for the integer, %% for the percent sign % (self-escaped) and %n for the line break character(s).
If I'm understanding your question correctly, you mean something like this?
String words[] = {"green", "eggs", "and", "ham"};
String response = "eggs or ham";
Mood mood = new Mood();
for(String foo : words)
{
if(response.contains(foo))
{
//Check if happy etc...
if(response.equals("green")
mood.sad++;
...
}
}
System.out.println("Success");
...
//CheckMood() etc... other methods.
Try to use tokens.
Every time that the program needs to compare the contents of a row from one array to the other array, just tokenize the contents in parallel and compare them.
Visit the following Java Doc page for farther reference: http://docs.oracle.com/javase/7/docs/api/java/util/StringTokenizer.html
or even view the following web pages:
http://introcs.cs.princeton.edu/java/72regular/Tokenizer.java.html

Number to words Mapping , Awe inspiring memory whim?

I need help for completing this little project
Program will take a phone number as an input and convert it into a proper English word.
Explaination:
There is some letters related to digits from 0-9 saved in a text file in first ten lines, something like
1 akl
2 dgh
3 qnm
4 rtu
5 zx
6 cvf
7 eip
8 wjs
9 yb
0 o
On line# 11 total number of words is present i-e 50000
after that, from line number 12 all 50000 words are present; one word per line.
Now program will take number(s) as an input form user until user enters -1
and then generate a proper English matching word from this text file.Each letter represents a digit from the list.
for example user enters
6182703
output will be :
Fashion
for more than 1 matching words , system will list all the words hyphen '-' seperated.
How should I start this, what approach should I use ?
If someone gives Pseudo code or hints .. It would be really great.
I would take a dictionary of words and sort it in a file by your needs.
e.g:
apple = 17717
cherry = 627449
Then go through the file with a search algorithm.
EDIT: or you could store the data in a Relational DB (http://hsqldb.org/ is simple) to avoid a bigger memory footprint. If you like the solution you also could investigate some key/value stores etc.
A lot of the detail in your question relates to the input spec, which is all pretty trivial.
After parsing your input, you're going to have a list of "candidate" words (all the words), and a mapping of digits to the set of characters it can be represented with.
List<String> words;
Map<Character, Set<Character>> digitMapping;
The simplest way of generating the word for a number is probably this: sequentially filter the list of candidates, testing if they match the input digits, and removing them otherwise. Something like this might do the trick (consider this pseudocode - I haven't tried compiling it):
List<String> getMatches(String inputDigits) {
// Take a copy of the word list. You don't want to ruin the list for the next caller
List<String> candidates = new ArrayList<String>(words);
for (Iterator<String> it = candidates.iterator(); it.hasNext() && !candidats.isEmpty(); ) {
String candidate = it.getNext();
for (int i = 0; i < inputDigits.length; ++i) {
Character c = new Character(candidate.charAt(i));
Character d = new Character(inputDigits.charAt(i));
if (!digitMapping.get(d).contains(c)) {
it.remove();
}
}
}
return candidates;
}
It will return all the words that match, so in your example, "555" will likely return an empty list. "6182703" might only return a single word, "fashion", while "202" might return several words in a list ("dog", "hog", "god"). You'll need to decide how you want to handle the zero and multiple cases.
Edit: Details on populating digitMapping:
The digitMapping will be something like:
Map<Character, Set<Character>> digitMapping = new HashMap<Character, Set<Character>>();
Then you'll need to grab a char and a String from the input. For the input line "1 akl", your char will be '1', while your String will be "akl". You're mapping from the character to the set of characters in the string, so will need to construct an empty set, put it into the map, then populate the set. Something like (again, I haven't even tried compiling this, so take it with a grain of salt):
private void addDigitToMap(char digit, String chars) {
Set<Character> set = new HashSet<Character>();
digitMapping.put(set);
for (char c : chars.toCharArray()) {
set.add(new Character(c));
}
}
So now the map will have an entry that points to a set of the characters it can be represented by.

Categories

Resources