Can we split a Java String Backwards? - java

I have a string
Product Description ID1 ID2 QTY Rate1 Rate2
I want do break it as
Product Description|ID1|ID2|QTY|Rate1|Rate2
The only common delimiter I could see is space. But product description can be any number of words. So I don't know how many spaces it will have. But the spaces in rest of the string is fixed.
Currently I split it into array, get its length and concat appropriately. Is there a more direct way?

It is not entirely clear what you mean by "more directly", but I'm assuming that you want to avoid the concatenation step.
One way would be as follows:
make an initial pass using Matcher.find(int) to find the position of the "N - 5th" separator. (This requires an int[] with 5 elements that you use as a sort of queue for the offsets determined using find.)
use String.substring to extract the strings before and after that separator
use String.split to split the 2nd substring to separate the last 5 fields.
If you kept the offsets and match lengths (from step #1) you could replace the split call in step #3 with a loop and substring calls.
(The reason you can't in general split "backwards" is that regexes cannot be matched backwards. Or more precisely, if you did that you might end up splitting differently to when you did it "forwards". This makes it non-trivial to avoid doing 2 passes. Obviously in your specific case, the separator regex would be "one-or-more-whitespace" which would work in both directions ... if there was a capability to do that in the Matcher API.).

Split it into an array as normal
You will end up with an array with n + 5 components where n is the number of words in the product description.
Then you know:
Rate2 = arr[len-1]
Rate1 = arr[len-2]
QTY = arr[len-3]
ID2 = arr[len-4]
ID3 = arr[len-5]
Then combine elements arr[0] to arr[len-6] adding a space in between them. This gives you your product description.
Alternatively you could use Collections.reverse() on the output array but both methods get the same result

Using a StringBuilder, this is probably as compact as you can get. The good thing is you don't have to juggle a lot of indices around. In fact, you don't have to juggle any.
String foo = "Product Description Test Potato ID1 ID2 QTY Rate1 Rate2 ";
foo = foo.trim(); // get rid of any extra spaces, in case we have any, as a safety check
//the code that does your work
StringBuilder bar = new StringBuilder(foo);
// we have to add the pipe 5 times
for (int i = 5; i > 0; i--) {
bar.setCharAt(bar.lastIndexOf(" "), '|');
}
foo = bar.toString();

// Product Description ID1 ID2 QTY Rate1 Rate2
// Product Description|ID1|ID2|QTY|Rate1|Rate2
String actWord = "Product it a middle data Description ID1 ID2 QTY Rate1 Rate2";
String outputWord = null;
String[] arr = actWord.split(" ");
StringBuffer buf = new StringBuffer();
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i] + "=" + (i < arr.length - 5));
if (i < arr.length - 5) {
buf.append(arr[i] + " ");
} else
buf.append("|" + arr[i]);
}
System.out.println("output - " + buf.toString().trim());
I hope this solves your req.

Reverse, the string.
Parse/Split based on space.
Re-reverse all the words as you pull them out of the collection of parsed/split words.
Not super elegant, but it should work.

Related

How to remove a trailing comma from a string (Java)

I have an array, which I use a for each loop to iterate through. I then store it in a string and then try to add a comma to it, but am struggling look for a "logic" based solution.
I am almost close to about 1 year's worth of Java under my belt, so most of the solutions I am trying to find to implement are mostly logic based (for now), since this is apart of Java MOOC's course. What are some options I can look at? What am I missing?
for(int number: array){
String thread = "";
thread += number + ", ";
System.out.print(thread);
}
You can use a Stream to achieve this result.
System.out.println(Arrays.stream(array).collect(Collectors.joining(",")));
I'm not sure the constraints of this project for your course, but if you're able, try a StringJoiner! It will add a delimiter automatically to separate items that are added to it. Another note, I think you're going to want to declare your String outside of your for loop. otherwise it resets every iteration.
StringJoiner joiner = new StringJoiner(",");
for(int number : array){
joiner.add(String.valueOf(number));
}
System.out.print(joiner.toString());
What I like to do when I'm just doing something simple and quick is this:
String thread = "";
for (int i = 0; i < array.length; i++) {
int number = array[i];
thread += number;
if (i < array.length - 1) {
thread += ", ";
}
}
Basically all it does is check that we aren't on the last index and append the comma only if it isn't the last index. It's quick, simple, and doesn't require any other classes.
Pressuming you had a string ending with a comma, followed by zero or more white spaces you could do the following. String.replaceAll() uses a regular expression to detect the replacement part.
\\s* means 0 or more white spaces
$ means at the end of the line
String str = "a, a, b,c, ";
str = str.replaceAll(",\\s*$","");
Prints
a, a, b,c

Java change full name to initial. last name

I have a database of player names that i need converted for me to be able to further work with them (for example: I need Antonio Brown converted to A. Brown). My problem is that there are also names that only consist of the first name (for example Antonio) Therefore i get an ArrayIndexOutOfBoundsException: 1, is there another way to get what i want and why does it even with the if condition stil split?
if(spalte[1].contains(" ")){
String[] me = spalte[0].split(" ", 2);
String na = me[0].substring(0);
name = na + ". " + me[1];
} else {
name = spalte[1];
}
Firstly, I highly recommend you to keep your code formatted and variables named properly. It helps not only others to understand a snippet better but also makes debugging a bit easier.
While working with arrays and String::split, you have to be careful with indices because they might overflow easily.
Do you need to make the code handle multiple spaces: Antonio Light Brown -> A. L. Brown? The steps are simple and practically the same for any number of names:
Split by a space delimiter
Shorten the n-1 first partitions
Concatenate the String back
Here is the code:
String split[] = name.trim().split(" "); // Trim the multiple spaces inside to avoid empty parts
StringBuilder sb = new StringBuilder(); // StringBuilder builds the String
for (int i=0; i<split.length; i++) { // Iterate the parts
if (i<split.length -1) { // If not the last part
sb.append(split[i].charAt(0)).append(". "); // Append the first letter and a dot
} else sb.append(split[i]); // Or else keep the entire word
}
System.out.println(sb.toString()); // StringBuilder::toString returns a composed String
Hypothetically: How would you handle names such as O'Neil or de Anthony? You can include the conditional concatenation in the for-loop.

Java: Split lines that are unequal

I tried looking for answers online, but I don't know how to word it correctly to find what I'm looking for. I have a file that I need to split but some lines are missing the regex I am trying to use.
The file I need to split looks like this:
A,106,Chainsaw 12"
D,102
d,104
a,107,Chainsaw 10"
I need to split it in three different sections, Letter, ID, Tool but 102 and 104 are missing the comma and Tool section. I've tried:
String[] sec = line.split(",");
and
String[] sec = line.split(",| \n");
And several other regex combinations, but none of them work. I get an AOB error on the line such as (below) because its missing.
...[0];
...[1];
String tool = sec[2]; //here
Any help is appreciated
Use String[] sec = line.split(","); and then test the length of the sec array
If you have 2 then you can use sec[0] and sec[1] but if you have 3 you can also use sec[2] If you have 0 then you have a empty line
You were on the right path with
String[] sec = line.split(",");
This function will return an Array with (in your example) either 2 or 3 elements. Then you could simply check for the length of the array
String[] sec = line.split(",");
int length = sec.length;
Here you can see what type of entry you have. Either length will be 2, which mean that it is a letter-ID pair, or it is 3, which means it is a letter, Id and tool entry.
If you need to be able to distinguish between more categories, you will have to put in an extra check. For example: Lets say your entry can be missing out one of the other two elements (not only the tool) and you could encounter an entry like:
a,Chainsaw 10
In this case you will furthermore need to read out the type of the single elements. The first thing that comes to my mind is, that you could check the first element in your array and check its length (should always be 1, since it is just a letter) and parse the second one into Integer (I assume id is always a number)
You are splitting on , and you have cases where input string is not present in same pattern.
So for this knid of situation, after splitting the string, every-time you have to check for the array length.
If it's less than desired length, then you cannot access desired element, because it's not present in the array.
For example:
When you do String[] sec = line.split(","); for A,106,Chainsaw 12, you will have 3 length, and you can access elements like sec[0],sec[1],sec[2].
When you split A,106, then you will get 2 as length, and elements present in the array are going to be sec[0],sec[1].
Example code:
import java.util.*;
public class ArrayListDemo {
public static void main(String args[]) {
ArrayList<String> lines= new ArrayList<String>();
lines.add("A,106,Chainsaw 12");
lines.add("A,106");
lines.add("A");
for(String str:lines){
String[] parts = str.split(",");
if(parts.length<2){
for(int i = 0 ; i < parts.length ; i++)
System.out.println("Splitted Item at index " + "[" + i + "]" + "::" + parts[i]);
}else{
for(int i = 0 ; i < parts.length ; i++)
System.out.println("Splitted Item at index " + "[" + i + "]" + "::" + parts[i]);
}
}
}
}
Hope that helps..

Solution critique: reversing words in a sentence

I had a technical interview and was given the following question:
Write a function that takes a sentence and returns the sentence with the words in reverse order (i.e. "hello world" becomes "world hello").
Here is the solution that I gave in Java:
/** Takes a sentence as an input and returns the sentence with the words in
* reversed order. */
private static String reverseSentence(String sentence) {
String[] words = sentence.split("\\s+");
String reversedString = "";
for (int k = words.length - 1; k >= 0; k -= 1) {
reversedString += " " + words[k];
}
return reversedString.substring(1);
}
I got to thinking that there has to be a more efficient way to solve this problem than this. I don't see this being asked in a technical interview for a top company if this solution turns out to be the best one and there isn't one more efficient/elegant.
Can anyone think of a better way to do this?
Here are better ways to concatenate the elements of the sentence: What's the most elegant way to concatenate a list of values with delimiter in Java?
Also, sentence.split("\\s+") only works when the input is clean (people do make typos). There is also the questions what should happen to punctuation. World! Hello, does look very odd.
Except the comment, to use a more efficient data structure (StringBuilder) to create the string, I don't think there is much you can do. The time complexity to reverse an array is n/2 (How do I reverse an int array in Java?) but afterwards you would still need to concatenate all of the items, which should be n - since you're concatenating them right away you get down from 3n/2 to only n.
Once you have reversed the array by swapping references (0 with length-1, etc) you can
String reversedSentence = String.join( " ", words );
But I think it is better to avoid a String array.
String s = "the quick brown fox";
StringBuilder sb = new StringBuilder();
int p = s.length();
int np = -1;
while( (np = s.lastIndexOf( " ", p-1 )) != -1 ){
sb.append( s.substring( np + 1, p ) ).append( " " );
p = np;
}
sb.append( s.substring( 0, p ) );
System.out.println( sb.toString() );
This assumes that the input string is properly "normalized".
Sometimes this question is asked with a constraint asking for it to be done without using any extra memory.
One answer to this modified question is:
First reverse each word in the sentence ("Hello World" -> "olleH dlroW")
Then reverse the entire sentence ("olleH dlroW" -> "World Hello")

Counting the occurrences of string in Java using string.split()

I'm new to Java. I thought I would write a program to count the occurrences of a character or a sequence of characters in a sentence. I wrote the following code. But I then saw there are some ready-made options available in Apache Commons.
Anyway, can you look at my code and say if there is any rookie mistake? I tested it for a couple of cases and it worked fine. I can think of one case where if the input is a big text file instead of a small sentence/paragraph, the split() function may end up being problematic since it has to handle a large variable. However this is my guess and would love to have your opinions.
private static void countCharInString() {
//Get the sentence and the search keyword
System.out.println("Enter a sentence\n");
Scanner in = new Scanner(System.in);
String inputSentence = in.nextLine();
System.out.println("\nEnter the character to search for\n");
String checkChar = in.nextLine();
in.close();
//Count the number of occurrences
String[] splitSentence = inputSentence.split(checkChar);
int countChar = splitSentence.length - 1;
System.out.println("\nThe character/sequence of characters '" + checkChar + "' appear(s) '" + countChar + "' time(s).");
}
Thank you :)
Because of edge cases, split() is the wrong approach.
Instead, use replaceAll() to remove all other characters then use the length() of what's left to calculate the count:
int count = input.replaceAll(".*?(" + check + "|$)", "$1").length() / check.length();
FYI, the regex created (for example when check = 'xyz'), looks like ".*?(xyz|$)", which means "everything up to and including 'xyz' or end of input", and is replaced by the captured text (either `'xyz' or nothing if it's end of input). This leaves just a string of 0-n copies the check string. Then dividing by the length of check gives you the total.
To protect against the check being null or zero-length (causing a divide-by-zero error), code defensively like this:
int count = check == null || check.isEmpty() ? 0 : input.replaceAll(".*?(" + check + "|$)", "$1").length() / check.length();
A flaw that I can immediately think of is that if your inputSentence only consists of a single occurrence of checkChar. In this case split() will return an empty array and your count will be -1 instead of 1.
An example interaction:
Enter a sentence
onlyme
Enter the character to search for
onlyme
The character/sequence of characters 'onlyme' appear(s) '-1' time(s).
A better way would be to use the .indexOf() method of String to count the occurrences like this:
while ((i = inputSentence.indexOf(checkChar, i)) != -1) {
count++;
i = i + checkChar.length();
}
split is the wrong approach for a number of reasons:
String.split takes a regular expression
Regular expressions have characters with special meanings, so you cannot use it for all characters (without escaping them). This requires an escaping function.
Performance String.split is optimized for single characters. If this were not the case, you would be creating and compiling a regular expression every time. Still, String.split creates one object for the String[] and one object for each String in it, every time that you call it. And you have no use for these objects; all you want to know is the count. Although a future all-knowing HotSpot compiler might be able to optimize that away, the current one does not - it is roughly 10 times as slow as simply counting characters as below.
It will not count correctly if you have repeating instances of your checkChar
A better approach is much simpler: just go and count the characters in the string that match your checkChar. If you think about the steps you need to take count characters, that's what you'd end up with by yourself:
public static int occurrences(String str, char checkChar) {
int count = 0;
for (int i = 0, l = str.length(); i < l; i++) {
if (str.charAt(i) == checkChar)
count++;
}
return count;
}
If you want to count the occurrence of multiple characters, it becomes slightly tricker to write with some efficiency because you don't want to create a new substring every time.
public static int occurrences(String str, String checkChars) {
int count = 0;
int offset = 0;
while ((offset = str.indexOf(checkChars, offset)) != -1) {
offset += checkChars.length();
count++;
}
return count;
}
That's still 10-12 times as fast to match a two-character string than String.split()
Warning: Performance timings are ballpark figures that depends on many circumstances. Since the difference is an order of magnitude, it's safe to say that String.split is slower in general. (Tests performed on jdk 1.8.0-b28 64-bit, using 10 million iterations, verified that results were stable and the same with and without -Xcomp, after performing tests 10 times in same JVM instances.)

Categories

Resources