Java String New Line Loop - java

I wrote a method that loops through a string and adds '/n' to create a line length that was given in the parameters. That description is not the best but it's hard to describe so look at the code below. Thanks in advance!
My Code:
public static String lineLength(String str, int length){
int totalLength = 0; //total length of the document
int lengthConst = 0; //constant value of the length for the loop
int nLength = 0; // length of \n = 2 characters
String work1, work2; //Strings to work with in the loop. Used as string buffers in substrings
if(str != null){
totalLength = str.length();
lengthConst = length;
}
if(length < 1){
throw new NullPointerException("Length must be >= 1");
}
/*
Main Loop: check again if length is not zero, check if totalLength is not zero,
check if pseudoCursor is not zero, check if length is less than or equal to totalLength
*/
while((length != 0) && (totalLength != 0) && (lengthConst != 0) && (length <= totalLength)){
work1 = str.substring(0, length); //store string of beginning to line length
work2 = str.substring(length + nLength, str.length()); //store string from length to end
work1 = work1.concat("\n"); //add new line
str = work1.concat(work2); //add work1 and work2 and store in str
nLength += 1; //nLength increases by 2 because we are going to add another \n
length += length;
}
return str;
}
When provided with the string "Daniel" and the new line length of 2 this is the run when printed to the console:
run:
Da
n
el
BUILD SUCCESSFUL (total time: 4 seconds)

I'd recommend using a for loop. I think it would be easier than what you are currently doing. Generally for loops go as such:
for(START POSITION, CONTROL, ITERATION PATTERN){
CODE
}
I'd read more about for loops here:
http://www.tutorialspoint.com/java/java_loop_control.htm
http://docs.oracle.com/javase/tutorial/java/nutsandbolts/for.html
The String object has a method .length() which will be used for the control of the loop. You want to iterate by 2 (because that's how you're separating it the words). You also want to start at 1 (usually the starting position is 0 but in this case we want 1):
String word = "Daniel";//starting word
String outputWord = "";//make it empty quotes so you can concatenate to it.
//if the size of word is less than 2, then print so
//else do the code below
for(int i = 1; i < word.length(); i = i+2){
outputWord = outputWord + word.get(i-1) + word.get(i) + "\n";
}
//check if the length was of word was odd. if so, then add the last character to outputWord
System.out.println(outputWord);
NOTE: This will only working assuming your word variable is at least 2 in size. I'll leave that error handling up to you to write. You'll also want to handle in odd length cases as well.

Here's a much simplified version
public static String lineLength(String str, int length) {
StringBuilder sb = new StringBuilder();
while(true) {
if(str.length() <= length) {
sb.append(str);
break;
}
sb.append(str.substring(0, length));
sb.append("\n");
str = str.substring(length);
}
return sb.toString();
}
You still need to understand what was wrong with your solution so that you learn from it and can apply that knowledge to the code you write in the future. Step through both this and your original code in a debugger and observe carefully what is happening.

Related

How to efficiently remove consecutive same characters in a string

I wrote a method to reduce a sequence of the same characters to a single character as follows. It seems its logic is correct while there is a room for improvement in terms of performance, according to my tutor. Could anyone shed some light on this?
Comments of aspects other than performance is also really appreciated.
public class RemoveRepetitions {
public static String remove(String input) {
String ret = "";
String last = "";
String[] stringArray = input.split("");
for(int j=0; j < stringArray.length; j++) {
if (! last.equals(stringArray[j]) ) {
ret += stringArray[j];
}
last = stringArray[j];
}
return ret;
}
public static void main(String[] args) {
System.out.println(RemoveRepetitions.remove("foobaarrbuzz"));
}
}
We can improve the performance by using StringBuilder instead of using string as string operations are costlier. Also, the split function is also not required (it will make the program slower as well).
Here is a way to solve this:
public static String remove(String input)
{
StringBuilder answer = new StringBuilder("");
int N = input.length();
int i = 0;
while (i < N)
{
char c = input.charAt(i);
answer.append( c );
while (i<N && input.charAt(i)==c)
++i;
}
return answer.toString();
}
The idea is to iterate over all characters of the input string and keep appending every new character to the answer and skip all the same consecutive characters.
Possible change which you could think of in your code is:
Time Complexity: Your code is achieving output in O(n) time complexity, which might be the best possible way.
Space Complexity: Your code is using extra memory space which arises due to splitting.
Question to ask: Can you achieve this output, without using the extra space for character array that you get after splitting the string? (as character by character traversal is possible directly on string).
I can provide you the code here but, it would be great if you could try it on your own, once you are done with your attempts
you can lookup for the best solution here (you are almost there)
https://www.geeksforgeeks.org/remove-consecutive-duplicates-string/
Good luck!
As mentioned before, it is much better to access the characters in the string using method String::charAt or at least by iterating a char array retrieved with String::toCharArray instead of splitting the input string into String array.
However, Java strings may contain characters exceeding basic multilingual plane of Unicode (e.g. emojis 😂😍😊, Chinese or Japanese characters etc.) and therefore String::codePointAt should be used. Respectively, Character.charCount should be used to calculate appropriate offset while iterating the input string.
Also the input string should be checked if it's null or empty, so the resulting code may look like this:
public static String dedup(String str) {
if (null == str || str.isEmpty()) {
return str;
}
int prev = -1;
int n = str.length();
System.out.println("length = " + n + " of [" + str + "], real length: " + str.codePointCount(0, n));
StringBuilder sb = new StringBuilder(n);
for (int i = 0; i < n; ) {
int cp = str.codePointAt(i);
if (i == 0 || cp != prev) {
sb.appendCodePoint(cp);
}
prev = cp;
i += Character.charCount(cp); // for emojis it returns 2
}
return sb.toString();
}
A version with String::charAt may look like this:
public static String dedup2(String str) {
if (null == str || str.isEmpty()) {
return str;
}
int n = str.length();
StringBuilder sb = new StringBuilder(n);
sb.append(str.charAt(0));
for (int i = 1; i < n; i++) {
if (str.charAt(i) != str.charAt(i - 1)) {
sb.append(str.charAt(i));
}
}
return sb.toString();
}
The following test proves that charAt fails to deduplicate repeated emojis:
System.out.println("codePoint: " + dedup ("😂😂😍😍😊😊😂 hello"));
System.out.println("charAt: " + dedup2("😂😂😍😍😊😊😂 hello"));
Output:
length = 20 of [😂😂😍😍😊😊😂 hello], real length: 13
codePoint: 😂😍😊😂 helo
charAt: 😂😂😍😍😊😊😂 helo

Count Words Using indexOf

I can't use arrays, only simple Java (if, for, while, substring, length, indexOf)
public int howManyWords(String s){
myString = "I have a dream";
int count = 1;
int length = 0;
while(count>=0){
count = myString.substring(String.valueOf(length),myString.indexOf(" "));
count++;
length = myString.indexOf(" ");
}
return count;
}
Should return 4
First of all, you made infinite loop, because count is 1, and you just increase it.
Second, you haven't even try to write this code in some IDE, because it would throw you a syntax error, because you are assigning string to int, when you do count = myString.substring()
So, instead of using count in loop, you can use myString.indexOf
something like this could work if you don't care what is going to happen with myString
int count = 0;
while(myString.indexOf(" ") >= 0) {
count++;
myString = myString.substring(myString.indexOf(" ") + 1)
}
return count;
Let's assume that the string you are testing does not contain leading or trailing spaces, because that affects the solution. The example string in your question does not contain leading or trailing spaces.
Simply call method indexOf(String, int) in a loop and in each iteration you set the int parameter to one more than what you got in the previous iteration. Once the value returned by method indexOf() is -1 (minus one), you are done. But don't forget to add the last word after you exit the loop.
String myString = "I have a dream";
int count = 0;
int index = 0;
while (index >= 0 && index < myString.length()) {
index = myString.indexOf(" ", index);
System.out.println("index = " + index);
if (index >= 0) {
index++;
count++;
}
}
if (index < 0) {
count++;
}
System.out.println("count = " + count);
Edited : Added missing else case.
Try the following code :
Remove the counted words from your string using the substring and indexOf, and increment the count in each iteration.
public int countWords(String s){
String myString = "I have a dream";
int count = 0;
int length = myString.length();
while(length>0){
if((myString.indexOf(" ")!=-1) && (myString.indexOf(" ")+1)<length){
myString = myString.subString(myString.indexOf(" ")+1);
count++;
length = myString.length();
}
else {
length = 0;
break;
}
}
return count;
}
PS: Conventionally, your method names should denote actions, hence I suggested it to be countWords instead of howManyWords.

searching a Char letter by letter

Trying to search for patterns of letters in a file, the pattern is entered by a user and comes out as a String, so far I've got it to find the first letter by unsure how to make it test to see if the next letter also matches the pattern.
This is the loop I currently have. any help would be appreciated
public void exactSearch(){
if (pattern==null){UI.println("No pattern");return;}
UI.println("===================\nExact searching for "+patternString);
int j = 0 ;
for(int i=0; i<data.size(); i++){
if(patternString.charAt(i) == data.get(i) )
j++;
UI.println( "found at " + j) ;
}
}
You need to iterate over the first string until you find the first character of the other string. From there, you can create an inner loop and iterate on both simultaneously, like you did.
Hint: be sure to look watch for boundaries as the strings might not be of the same size.
You can try this :-
String a1 = "foo-bar-baz-bar-";
String pattern = "bar";
int foundIndex = 0;
while(foundIndex != -1) {
foundIndex = a1.indexOf(pattern,foundIndex);
if(foundIndex != -1)
{
System.out.println(foundIndex);
foundIndex += 1;
}
}
indexOf - first parameter is the pattern string,
second parameter is starting index from where we have to search.
If pattern is found, it will return the starting index from where the pattern matched.
If pattern is not found, indexOf will return -1.
String data = "foo-bar-baz-bar-";
String pattern = "bar";
int foundIndex = data.indexOf(pattern);
while (foundIndex > -1) {
System.out.println("Match found at: " + foundIndex);
foundIndex = data.indexOf(pattern, foundIndex + pattern.length());
}
Based on your request, you can use this algorithm to search for your positions:
1) We check if we reach at the end of the string, to avoid the invalidIndex error, we verify if the remaining substring's size is smaller than the pattern's length.
2) We calculate the substring at each iteration and we verify the string with the pattern.
List<Integer> positionList = new LinkedList<>();
String inputString = "AAACABCCCABC";
String pattern = "ABC";
for (int i = 0 ; i < inputString.length(); i++) {
if (inputString.length() - i < pattern.length()){
break;
}
String currentSubString = inputString.substring(i, i + pattern.length());
if (currentSubString.equals(pattern)){
positionList.add(i);
}
}
for (Integer pos : positionList) {
System.out.println(pos); // Positions : 4 and 9
}
EDIT :
Maybe it can be optimized, not to use a Collection for this simple task, but I used a LinkedList to write a quicker approach.

How to tackle the Codingbat String-2 oneTwo challenge?

Here is the problem statement:
Given a string, compute a new string by moving the first char to come after the next two chars, so "abc" yields "bca". Repeat this process for each subsequent group of 3 chars, so "abcdef" yields "bcaefd". Ignore any group of fewer than 3 chars at the end.
Here is my code:
// oneTwo("abc") → "bca"
// oneTwo("tca") → "cat"
// oneTwo("tcagdo") → "catdog"
public String oneTwo(String str) {
String x = "";
if (str.length() < 3) {
return "";
// return empty
} else if (str.length() == 3) {
String s = str.substring(1, str.length());
x = s + str.substring(0, 1); // last two + first char
} else if (str.length() > 3) {
int third = 2;
// start with the third element index of 2
for (int i = 0; i < str.length(); i++) {
if (i == third) {
// given three chars substring first char
// substring last two chars and add that to x
x += (str.substring(third - 1, third + 1) +
str.substring(third - 2, third - 2 + 1));
third += 3;
//work with this line but why??????
}
//third +=3;
// doesn't work with this line but why???????
}// end of for loop
}
return x;
// return modified string x
}
With third +=3 inside of if statement work but when I put that outside of if statement I don't get the desired output. I don't really understand why?
Hope this helps:
public String oneTwo(String str) {
String str2 = "";
for(int i=0; i<str.length()-2; i+=3) {
str2 = str2+str.substring(i+1,i+3)+str.charAt(i);
}
return str2;
}
Because putting it outside the loop will cause third to be increased far too often. After the first iteration i is 0, third is 5, next iteration yields i=1, third=8; i=2, third=11; i=3, third=14, etc. -> i will never reach third.
I would improve your code by dropping the entire if-statement, remove third all together and simply increment by 3 in the for-loop:
for( int i = 2; i < str.length(); i+=3){
x += (str.substring(third-1, third+1) +
str.substring(third-2, third-2 + 1));
}
If I am not misinterpreting your code you are missing logic for leaving the last characters alone if they are not part of group of three characters.
If you face such effects take a piece of paper and write down the values of the variables after each line of your code.
The if block creates an alternative execution path if the condition is true which is in every third loop iteration.
Anything behind the if block is executed in every loop iteration.
So when the line in question is inside the if block (before the closing brace) the value in variable third is only changed every third loop iteration.
When you move the line behind the closing brace the assignment is outside the if block and therefore executed every loop iteration.
For the comment = //work with this line but why??????
The value of "third" variable gets changed in the for loop only with i is equal to third character, otherwise the value of third will keep on increasing eg.
when i = 0, third = 2
when i = 1, third = 5
when i = 2, third = 8
so the if statement never gets triggered and hence it doesn't work. Hope this makes sense.
PS - I highly recommend using IDE debugger to understand this properly.
PS - It's better to use charAt method as compared for substring method for performance reason
public String oneTwo(String str) {
String temp = "";
String result = "";
int i = 0;
while (str.substring(i).length() >= 3) {
temp = str.substring(i, i + 3);
result += temp.substring(1) + temp.charAt(0);
i += 3;
}
return result;
}
public String oneTwo(String str) {
String str1 = "";
if(str.length()<3){
return str1;
}else if(str.length()>=3){
for(int i =0; i<str.length()-2; i=i+3){
str1 = str1 + str.substring(i+1,i+3)+ str.substring(i,i+1);
}
}
return str1;
}
public String oneTwo(String str) {
if(str.length()<3)return "";
return str.substring(1,3)+str.substring(0,1)+oneTwo(str.substring(3));
}
this is fairly simple as a recursive problem
public String oneTwo(String str) {
String newThreeChars = "";
if(str.length()<3){
return newThreeChars;
}
for(int i=0; i<str.length()/3; i+=3){
String threeChars = str.substring(i,i+3);
String redesigned = threeChars.substring(1) + threeChars.charAt(0);
newThreeChars +=redesigned;
}
return newThreeChars;
}
Another solution to look at...
public String oneTwo(String str) {
int i = 0;
String result = "";
Character tmpChar = '\0';
while(i <= str.length()-3){
tmpChar = str.charAt(i);
result = result + str.charAt(i+1) + str.charAt(i+2) + tmpChar;
tmpChar = '\0';
i = i + 3;
}
return result;
}
First, We loop through each letter of the given String just stopping shy of the last two letters because the word we are looking for is three letters long. Then, we are returning true if there is two letter "b"'s exactly one character apart.
public boolean bobThere(String str) {
for (int i = 0; i < str.length() - 2; i++) {
if (str.charAt(i) == 'b' && str.charAt(i+2) == 'b')
return true;
}
return false;
}
For string concatenation in a loop use StringBuilder:
public String oneTwo(String str) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < str.length() - 2; i += 3) {
sb.append(str.charAt(i + 1)).append(str.charAt(i + 2)).append(str.charAt(i));
}
return sb.toString();
}

Breaking string into multiple lines in Java

I have a single line string of length n, which I want to split into maximum of 3 lines. Each line can have a maximum of 45 chars, after which I want to add a new-line char ("\n"). The 3rd line can have a maximum of 42 chars after which I need to include 3 dots (...) if the string goes beyond that, thus making the total characters in the 3rd line 45 as well.
The condition is that the new line character should not be added in the middle of a word. How do I do this efficiently? This operation is just a small part of the entire program, but will be called repeatedly. So I'm not sure if I should actually bother about the efficiency.
What I'm doing right now is that I first figure out where the spaces between words are and then add it to a List. I then iterate through the list and find 3 indices each representing the end word of each line. So the first index will be the space closest to 45, the next closest to 90, and the third closest to 135. I then use these indices to split the actual string, and add "\n" and "..." respectively. This is my code:
//maxCharsPerLine will be 45
public String splitString(String input, int maxCharsPerLine){
String output = "";
ArrayList<Integer> spaces = new ArrayList<Integer>();
// Logic to figure out after which word the sentence should be split so that we don't split in middle of a word
for(int index = 0; index < input.length(); index++){
if(input.charAt(index)==' '){
spaces.add(index);
}
}
//add index of last word of string
spaces.add(input.length());
int index1 = 0; int index2 = 0; int index3 = 0;
for(Integer index : spaces){
// find word closest to and less than maxCharsPerLine. This index will be used to find the last word in line1
if(index<=maxCharsPerLine)
index1 = index;
// find word closest to and less than 2*maxCharsPerLine. This index will be used to find the last word in line2
else if(index<=2*maxCharsPerLine)
index2 = index;
// find word closest to and less than 3*maxCharsPerLine, but exclude 3 chars for adding the dots (...). This index will be used to find the last word in line3
else if(index<=(3*maxCharsPerLine)-3)
index3 = index;
}
if(input.length()>maxCharsPerLine){
if(index1 > 0)
output = input.substring(0, index1);
if(index2 > 0)
output += "\n"+input.substring(index1+1, index2);
if(index3 > 0){
output += "\n"+input.substring(index2+1, index3);
if(input.length()>3*maxCharsPerLine)
output += "...";
}
}
//if length of input is < 45, just return the input
else
output = input;
return output;
}
Not sure in which scenarios this will fail. Is there a better way to do this?
Thanks.
You can use WordUtils.wrap method of Apache Commans Lang if 3 dots are not be considered for wrapping the line.
WordUtils.wrap(str, 45)
Code
public class test3 {
public static void main(String[] args) {
String S = "The condition is that the new line should not be added in the middle of a word. How do I do this efficiently? This operation is just a small part of the entire program, but will be called repeatedly. So I'm not sure if I should actually bother about the efficiency";
String Op = "";
String Op1 = "";
String Op2 = "";
String Op3 = "";
String Temp[] = S.split(" ");
int max_size_1 = 45;
int max_size_2 = 45;
int max_size_3 = 42;
int length = 0;
for (int i = 0; i < Temp.length; i++) {
length = length + Temp[i].length()+1;
if(length <= max_size_1) Op1 = Op1 + Temp[i]+" ";
else if(length <= Op1.length()+max_size_2) Op2 = Op2 +Temp[i]+" ";
else if(length <= Op1.length()+Op2.length()+max_size_3) Op3 = Op3 + Temp[i]+" ";
else {Op3 = Op3 +'\b' + "..."; i =Temp.length ; } //backspace
}
Op = Op1+"\n"+Op2+"\n"+Op3;
System.out.println(Op);
System.out.println(Op1.length()+" "+Op2.length()+" "+Op3.length()+" ");
}}
Output
The condition is that the new line should
not be added in the middle of a word. How do
I do this efficiently? This operation...
42 45 45
Here another solution, though it might be corrupted and needs to be edited.
int sizeOfString = input.lenght();
//the maximum lenght of a String
int aPartialStringLenght = 45;
String firstString;
String secondString;
String thirdString;
for(int x = 1; x <= 3; x++){
// looks for the last space before your 45th character
//sets the lenght for the third String to max. 42characters
if(x == 3){
aPartialStringLenght = 42;
}
while(!input.charAt(aPartialStringLenght*x).equals(" ")){
aPartialStringLenght -=1;
}
switch(x){
// gets the substring till your first partialString
case 1: firstString = input.substring(0, aPartialStringlenght);
aPartialStringLenght = 45;
// gets the substring from the end of your first partialString till the end of your second partialString
case 2: secondString = input.substring(firstString.lenght(), aPartialStringLenght + firstString.lenght());
aPartialStringLenght = 45;
// gets the substring from the end of your second partialString till till the end of your third partialString + "..."
case 3 thirdString = input.substring(firstString.lenght()+secondString.lenght(), aPartialStringLenght + firstString.lenght()+ secondString.lenght() )+"..."
aPartialStringLenght = 45;
}
}
Based on surya answer
public class test3 {
public static void main(String[] args) {
String S = "The condition is that the new line should not be added in the middle of a word. How do I do this efficiently? This operation is just a small part of the entire program, but will be called repeatedly. So I'm not sure if I should actually bother about the efficiency";
String F = WordUtils.wrap(S, 45);
String[] F1 = F.split(System.lineSeparator());
System.out.println(F1[0]);
System.out.println(F1[1]);
F1[2] = F1[2] +'\b'+'\b'+'\b'+"...";
System.out.println(F1[2]);
}
}
Output
The condition is that the new line should not
be added in the middle of a word. How do I do
this efficiently? This operation is jus...
My proposal is highly efficient, because:
It needs just two objects: the final string and a temporary StringBuilder, which is pre-sized,
And it does not waste time in pre-processing: Processes each character just once, and decides on the fly what to do.
And it is also flexible, because all the involved data are received as parameters:
public final class LinesSplitter
{
private LinesSplitter(){}
private static final char NL='\n';
public static String splitInLines(String text, int maxLineLength, int maxLines, String lastLineSuffix)
{
StringBuilder output=new StringBuilder((1 + maxLineLength) * maxLines);
int p=0;
int startOfLine=0;
int lastBlank=0;
int lastNonBlank=0;
int len=text.length();
String neededSuffix=text.length() > maxLineLength * maxLines
? lastLineSuffix
: "";
int lines=0;
while (lines < maxLines && p < len)
{
char c=text.charAt(p);
if (Character.isWhitespace(c))
{
lastBlank=p;
lastNonBlank=1 + p;
}
else if (p < len)
{
int maxLengthForCurrentLine=getMaxLength(maxLineLength, maxLines, 1 + lines, neededSuffix);
if (p - startOfLine == maxLengthForCurrentLine)
{
output.append(text, startOfLine, lastBlank);
String suffix=getSuffix(maxLineLength, maxLines, 1 + lines, neededSuffix);
if (!suffix.isEmpty())
{
output.append(suffix);
}
else
{
output.append(NL);
}
lines++;
startOfLine=lastNonBlank;
}
}
p++;
}
if (lines < maxLines && p - startOfLine > 0)
{
output.append(text, startOfLine, len);
}
return output.toString();
}
private final static int getMaxLength(int maxLineLength, int maxLines, int currentLine, String lastLineSuffix)
{
return currentLine == maxLines
? maxLineLength - lastLineSuffix.length()
: maxLineLength;
}
private final static String getSuffix(int maxLineLength, int maxLines, int currentLine, String lastLineSuffix)
{
return currentLine == maxLines
? lastLineSuffix
: "";
}
}
The only possible drawback is that it does not support several adjacent blanks.

Categories