I have a small problem that looks unsolvable:
GOAL: Justify lines (strings) in an ArrayList, by adding whitespaces to single whitespace characters, as much as needed for the text to get justified.
package com.mycompany.app;
import java.util.ArrayList;
import java.util.List;
public class MaxLengthLine {
String[] words;
int size;
int qtySpaces;
public MaxLengthLine (String text, int size){
this.words = text.split(" ");
this.size = size;
}
List<String> lines = new ArrayList<String>();
public void lineResize() {
int index = 0;
for (int i = 0; i < words.length - index; i++){
String curLine = "";
while((curLine + words[index]).length() <= size){
curLine += words[index] + " ";
index++;
}
curLine = curLine.substring(0, curLine.length()-1);
lines.add(curLine);
}
String curLine = "";
while(index < words.length){
curLine += words[index] + " ";
index++;
}
curLine = curLine.substring(0, curLine.length()-1);
lines.add(curLine);
}
public void lineJustify() {
for (int i = 0; i < lines.size(); i++){
while (lines.get(i).length() < size){
String test = lines.get(i).replaceFirst(" ", " ");
lines.set(i, test);
}
}
}
public String getTextFull (){
String output = "";
for(int i = 0; i < lines.size();i++){
output += lines.get(i) + "\n";
}
while (output.contains(" ")){
output = output.replace(" ", " ");
}
return output;
}
}
This code is the most straightfoward solution I thought at first (besides I have already tried plenty of others), but for some reason the result keeps coming the same.
Actual output:
In the beginning God created the heavens
and the earth. Now the earth was
formless and empty, darkness was over
the surface of the deep, and the Spirit
of God was hovering over the waters.
And God said, "Let there be light," and
there was light. God saw that the light
was good, and he separated the light
from the darkness. God called the light
"day," and the darkness he called
"night." And there was evening, and
there was morning - the first day.
Desired output:
In the beginning God created the heavens
and the earth. Now the earth was
formless and empty, darkness was over
the surface of the deep, and the Spirit
of God was hovering over the waters.
And God said, "Let there be light," and
there was light. God saw that the light
was good, and he separated the light
from the darkness. God called the light
"day," and the darkness he called
"night." And there was evening, and
there was morning - the first day.
Edit: The input:
In the beginning God created the heavens and the earth. Now the earth was formless and empty, darkness was over the surface of the deep, and the Spirit of God was hovering over the waters.
And God said, "Let there be light," and there was light. God saw that the light was good, and he separated the light from the darkness. God called the light "day," and the darkness he called "night." And there was evening, and there was morning - the first day.
(I already have code that breaks lines correctly at 40 chars without breaking words, so the last part is that function to justify the text to 40 chars)
EDIT 2: I changed that piece of code for the whole class to be more clear, the size being set on my teste is 40.
public static List<String> justifyLines(String input, int lineLength) {
String[] words = input.split(" ");
List<String> result = new ArrayList<>();
StringBuilder line = new StringBuilder();
//here we store positions of all spaces in the current line to add more spaces there
List<Integer> spacesPositions = new ArrayList<>();
for (String word : words) {
if (word.length() <= lineLength - line.length()) {
line.append(word).append(" ");
spacesPositions.add(line.length() - 1);
} else {
result.add(justifyLine(line, lineLength, spacesPositions));
line.setLength(0);
spacesPositions.clear();
line.append(word).append(" ");
spacesPositions.add(line.length() - 1);
}
}
if (line.length() > 0) {
result.add(justifyLine(line, lineLength, spacesPositions));
}
return result;
}
private static String justifyLine(StringBuilder line, int lineLength, List<Integer> spacesPositions) {
//if line ends with space - remove it
if (line.lastIndexOf(" ") == line.length() - 1) line.setLength(line.length() - 1);
int spacesToAdd = lineLength - line.length();
for (int j = 0; j < spacesToAdd; j++) {
//It's the most complicated part, but I'll try to explain
line.insert(
// We're adding one space to each space in the line and then, if there are still spaces to insert,
// repeating this process from the beginning - that's why we're using %
spacesPositions.get(j % (spacesPositions.size() - 1))
// But each time we insert a new space, we need to take it into account for the following positions
// j % (spacesPositions.size() - 1) is the number of space in the line
// j / (spacesPositions.size() - 1) + 1 is the iteration number
+ j % (spacesPositions.size() - 1) * (j / (spacesPositions.size() - 1) + 1), " ");
}
return line.toString();
}
So for (String s : justifyLines("In the beginning...", 40)) System.out.println(s); prints:
In the beginning God created the heavens
and the earth. Now the earth was
formless and empty, darkness was over
the surface of the deep, and the Spirit
of God was hovering over the waters. And
God said, "Let there be light," and
there was light. God saw that the light
was good, and he separated the light
from the darkness. God called the light
"day," and the darkness he called
"night." And there was evening, and
there was morning - the first day.
From Java String class:
public String replaceFirst(String regex,
String replacement)
Replaces the first substring of this string that matches the given regular expression with the given replacement.
So you need to give it a regex, not the space itself. In java regex for a whitespace is "\s"
String test = lines.get(i).replaceFirst("\\s", " ");
Also, as something to consider, replaceFirst only replaces the first substring that matches the regex, so this code will add whitespace only to the first whitespace you find, not evenly distributed like you want it to be (because the first space of the double space " " will still match to the regex "\s".)
Do check on this.
Related
I'm trying to return the middle 3 characters of a word using the substring method but how do I return the middle 3 letters of a word if the word can be any size (ODD only)?
My code looks like this.
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scnr = new Scanner(System.in);
String inputWord;
inputWord = scnr.next();
System.out.println("Enter word: " + inputWord + " Midfix: " + inputWord.substring(2,5));
}
}
The reason I have a 2 and 5 in the substring method is because I have tried it with the word "puzzled" and it returned the middle three letters as it was supposed to do. But if I try, for instance "xxxtoyxxx", It prints out "xto" instead of "toy".
P.S. Please don't bash me I'm new to coding :)
Consider the following code:
String str = originalString.substring(startingPoint, startingPoint + length)
To determine the startingPoint, we need to find the middle of the String and go back half the number of characters as the length we want to retrieve (in your case 3):
int startingPoint = (str.length() / 2) - (length / 2);
You could even build a helper method for this:
private String getMiddleString(String str, int length) {
if (str.length() <= length) {
return str;
}
final int startingPoint = (str.length() / 2) - (length / 2);
return "[" + str.substring(startingPoint, startingPoint + length) + "]";
}
Complete Example:
class Sample {
public static void main(String[] args) {
String text = "car";
System.out.println(getMiddleString(text, 3));
}
private static String getMiddleString(String str, int length) {
// Just return the entire string if the length is greater than or equal to the size of the String
if (str.length() <= length) {
return str;
}
// Determine the starting point of the text. We need first find the midpoint of the String and then go back
// x spaces (which is half of the length we want to get.
final int startingPoint = (str.length() / 2) - (length / 2);
return "[" + str.substring(startingPoint, startingPoint + length) + "]";
}
}
Here, I've put the output in [] brackets to reflect any spaces that may exist. The output of the above example is: [ppl]
Using this dynamic approach will allow you to run the same method on any length of String. For example, if our text String is "This is a much longer String..." our output would be: [ lo]
Considerations:
What if the input text has an even number of characters, but the length is odd? You would need to determine if you want to round the length up/down or return a slightly off-center set of characters.
I think what you can do is to calculate the string length then divided by 2. This gives you the string in the middle, then you can subtract one to the start and add 2 to the end. If you want to get the first two for an odd string, then subtract 2 to the start index and add 1 to the end.
String word_length = inputWord.length()/2;
System.out.println("Enter word: " + inputWord + " Midfix: " + inputWord.substring((word_length-1, word_length+2));
Hope this helps.
This will get the middle of the string, and return the characters at the middle, and +- 1 from the middle index.
public static String getMiddleThree(String str) {
int position, length;
if (str.length() % 2 == 0) {
position = str.length() / 2 - 1;
length = 2;
} else {
position = str.length() / 2;
length = 1;
}
int start = position >= 1 ? position - 1 : position;
return str.substring(start, position + 1);
}
The work left you have to do is make sure the end position is not greater than the length of the string, otherwise, choose the position as the final index
In my code I have a variable, points, that increments based on the consanants and vowels in strings inputted. The method parseSentence is supposed to increase points per word but also ignore spaces.
I've tried running a debugger to see where the problem is but the debugger dies when it reaches the for loop in parseSentence. The method makes the point variable's value the word's point value instead of adding it to the variable. What could be causing this?
import java.util.*;
public class WordGolf1 {
public static int points = 1;
public static void main(String[] args) {
String Input;
System.out.println("Enter word: ");
Scanner sc = new Scanner(System.in);
Input = sc.nextLine();
System.out.println("Not enough points. " + (100 - points) + " needed.");
while (points < 100) {
System.out.println("Enter word: ");
Input = sc.nextLine();
parseSentence(Input);
System.out.println(points + ": points");
System.out.println("Not enough points. " + (100 - points) + " needed.");
}
boolean overshot = true;
Loop:
while (overshot = true) {
if (points == 100) {
overshot = false;
break Loop;
}
points = 100 - (points - 100);
System.out.println("Overshot by " + (points - 100) + " points.");
Input = sc.nextLine();
parseSentence(Input);
}
System.out.println("Congratulations you win!");
sc.close();
}
public static int parseSentence(String input) {
String[] pieces = input.split("\\s+");
for (int y = 0; y < pieces.length; y++) {
if (pieces.length > 1) {
if (y == 0) {
parseWord(input);
} else {
parseWord(input, y);
}
} else {
parseWord(input);
}
}
return points;
}
public static int parseWord(String input) {
String[] pieces = input.split("\\s+");
String charList = "aeiouyAEIOUY";
String consanantList
= "bcdfghjklmnpqrstvwxzBCDFGHJKLMNPQRSTVWXZ";
int pointsTemp = 1;
for (int x = 0; x < pieces[0].length(); x++) {
if (charList.indexOf(pieces[0].charAt(x)) != -1) {
pointsTemp *= 2;
} else if (consanantList.indexOf(pieces[0].charAt(x))
!= -1) {
pointsTemp++;
}
}
points = pointsTemp;
return points;
}
public static int parseWord(String input, int number) {
String[] pieces = input.split("\\s+");
String charList = "aeiouyAEIOUY";
String consanantList
= "bcdfghjklmnpqrstvwxzBCDFGHJKLMNPQRSTVWXZ";
int pointsTemp = 1;
for (int x = 0; x < pieces[number].length(); x++) {
if (charList.indexOf(pieces[number].charAt(x)) != -1) {
pointsTemp *= 2;
} else if (consanantList.indexOf(pieces[number].charAt(x)) != -1) {
pointsTemp++;
}
}
points += pointsTemp;
return points;
}
}
You are not using the value returned by the parseSentence method.
Edit: I tried to rewrite this to be as close your original code with making the changes I feel where necessary.
Now Obviously your teacher has requirements and we can't go against that, but some points of interest you should keep in mind.
Multi Splitting
In your example you split the text to get the amount of words. Then instead of looping the already split text. You are sending the original input and then splitting it again. The "Double" splitting is why you needed "three" methods. If you don't double split you can simply loop the length from the single split and just use a single ParseWord method.
Deducting Values
In your example you take away 100 if the player overshot. The problem with this is let's say the person received a score like 200. Then it would loop twice to lower the value submitting the "You overshot message" twice. However let's say by some magical way a score of 100,000,000 was received. Then as you can see we would loop 1 million times to deduct this value essentially creating an not infinite but might as well be infinite loop.
To resolve this problem we simply do the below.
Value = Value % 100.
This will give us the remainder of our Value between 0 and 99. I.e. 167 will equal 67 and 12384 will be equal 84.
Using String (IndexOf)
What this does is takes the Character you provided and loop iterates over the String you provided. The worst case is 12 loops. There's also a lot of other stuff String and IndexOf do that is extra work and I recommend staying away from it if you can.
The alternative solution which I did is take the character and use " | 32" on it. I'm not going to go deep into how bits work, but basically these characters are 8 bit values but we only use 7 of it's bits ranging from 32 to 127. The amount of bits is like the power of 2. so 2^7 = 128 and 2^8 = 256. When we perform the "|" we are turning a bit on so if it's already on it won't change the value.
So in our example let's say we have the value 64.
This is bit 6 turned on. Now we want to turn on bit 5 "32" so the value becomes 96, but if we already had the value 96 and we turn bit 32 on it will still be 32.
Full List of ASCII Characters..
https://www.ascii-code.com/
The Game Loop
In your example you created "TWO" game loops the first one is when you start off, but once you overshot your score you enter the second loop and forget the first one. The problem is now your "Enter Words" and "You Undershot" code are never used anymore. So all someone will see is the line to enter text with no information on what to do or what occurred unless they overshot then they get the overshot message.
To fix this I made a single Game Loop which processes until the code ends via the SCORE == 100. You can see in the code that we begin every game loop with "Enter Words: " and parse the sentence. Then we add up our score and compare. If we undershot we simply restart the loop and try again. If we overshot we reduce the score and try again. If we succeeded we prompt the user if they would like to play again or end the game. Playing again will set the SCORE to 0 and start over the loop. Ending the game will "BREAK" the loop and cause it to end.
The Full Working Code With Recommended Changes
Feel free to comment if you need additional assistance.
import java.util.*;
public class WordGolf1
{
private static int SCORE = 0;
public static void main(String[] args)
{
Scanner sc = new Scanner(System.in);
while (true)
{
System.out.print("\n\nEnter word: ");
ParseSentence(sc.nextLine());
if (SCORE == 100)
{
System.out.print("\nYou Won! Would you like to play again: Y/N?");
if ((sc.nextLine().charAt(0) | 32) == 'y')
{
SCORE = 0;
System.out.print("\nResetting Game...");
} else {
break;
}
}
else
{
if (SCORE > 100)
{
int overshot = SCORE - 100;
SCORE = SCORE % 100;
System.out.print("\nYou Overshot By " + overshot + " Points. You now have " + SCORE + " points.");
} else {
System.out.print("\nYou currently have " + SCORE + " points you need " + (100 - SCORE) + " more.");
}
}
}
}
private static int ParseSentence(String input)
{
String[] split = input.split(" ");
for (Strng s : input)
SCORE += ParseWord(s);
}
private static int ParseWord(String word)
{
int value = 1;
for (int i = 0; i < word.length(); ++i)
{
int c = (int)word.charAt(i) | 32;
if (c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u')
{
value *= 2;
} else {
value += 1;
}
}
return value;
}
}
I have been searching for solution to find strings like this howareyou in sentence and remove them from it. For example:
We have a sentence - Hello there, how are you?
And compound - how are you
As a result I want to have this string - Hello there, ? With compound removed.
My current solution is splitting string into words and checking if compound contains each word, but it's not working well, because if you have other words that match that compound they will also be removed, e.g.:
If we will look for foreseenfuture in this string - I have foreseen future for all of you, then, according to my solution for will also be removed, because it is inside of compound.
Code
String[] words = text.split("[^a-zA-Z]");
String compound = "foreseenfuture";
int startIndex = -1;
int endIndex = -1;
for(String word : words){
if(compound.contains(word)){
if(startIndex == -1){
startIndex = text.indexOf(word);
}
endIndex = text.indexOf(word) + word.length() - 1;
}
}
if(startIndex != -1 && endIndex != -1){
text = text.substring(0, startIndex) + "" + text.substring(endIndex + 1, text.length() - 1);
}
So, is there any other way to solve this?
I'm going to assume that when you compound you only remove whitespace. So with this assumption "for,seen future. for seen future" would become "for,seen future. " since the comma breaks up the other compound. In this case then this should work:
String example1 = "how are you?";
String example2 = "how, are you... here?";
String example3 = "Madam, how are you finding the accommodations?";
String example4 = "how are you how are you how are you taco";
String compound = "howareyou";
StringBuilder compoundRegexBuilder = new StringBuilder();
//This matches to a word boundary before the first word
compoundRegexBuilder.append("\\b");
// inserts each character into the regex
for(int i = 0; i < compound.length(); i++) {
compoundRegexBuilder.append(compound.charAt(i));
// between each letter there could be any amount of whitespace
if(i<compound.length()-1) {
compoundRegexBuilder.append("\\s*");
}
}
// Makes sure the last word isn't part of a larger word
compoundRegexBuilder.append("\\b");
String compoundRegex = compoundRegexBuilder.toString();
System.out.println(compoundRegex);
System.out.println("Example 1:\n" + example1 + "\n" + example1.replaceAll(compoundRegex, ""));
System.out.println("\nExample 2:\n" + example2 + "\n" + example2.replaceAll(compoundRegex, ""));
System.out.println("\nExample 3:\n" + example3 + "\n" + example3.replaceAll(compoundRegex, ""));
System.out.println("\nExample 4:\n" + example4 + "\n" + example4.replaceAll(compoundRegex, ""));
The output is as follows:
\bh\s*o\s*w\s*a\s*r\s*e\s*y\s*o\s*u\b
Example 1:
how are you?
?
Example 2:
how, are you... here?
how, are you... here?
Example 3:
Madam, how are you finding the accommodations?
Madam, finding the accommodations?
Example 4:
how are you how are you how are you taco
taco
You can also use this to match any other alpha-numeric compound.
in the program below you can see that i've allowed the input of the user to give a direction such as n100 which will draw a line north and move it 100 spaces but How am I able to allow the sketch program to do diagonal lines as well as straight lines, I understand I am able to change the input to (0,2) to allow diagonal lines by using something like ne but then my program doesn't like when I use directions such as n, e, s, w.
What can I do to allow both lines?
this is the code below:
boolean okToProcess = true;
String message = "";
int colourInt;
String input = in.getText();
String direction = input.substring(0, 1);
String distance = input.substring(1);
double distanceAsDouble = 0;
if (direction.equals("n"))
t.heading(0);
else if (direction.equals("ne"))
t.heading(45);
else if (direction.equals("e"))
t.heading(90);
else if (direction.equals("se"))
t.heading(135);
else if (direction.equals("s"))
t.heading(180);
else if (direction.equals("sw"))
t.heading(225);
else if (direction.equals("w"))
t.heading(270);
else if (direction.equals("nw"))
t.heading(315);
else {
okToProcess = false;
message += "bad direction: " + direction + " ";
}
if (isNumeric(distance)) {
distanceAsDouble = Double.parseDouble(distance);
}
else{
okToProcess = false;
message += "bad distance: " + distance;
}
if (okToProcess) {
if (!EtchASketchClipped(t, distanceAsDouble)) {
t.setLineWidth(3);
If you use
String direction = input.substring(0, 1);
String distance = input.substring(1);
you are storing and comparing only the first character of the string and eventually assign an invalid number to distance, as if the direction is diagonally, the second character is prepended to direction. Use String.startsWith() to check for a given direction. Inside the if-statement, decide wether to start the distance at the second or third character. You can also just use input as a value to check.
...
String distance ;
double distanceAsDouble = 0;
if (input.startsWith("n")) {
t.heading(0);
distance = input.substring(1);
} else if (input.startsWith("ne")) {
t.heading(45);
distance = input.substring(2);
} else if ...
String direction = input.replaceFirst("^(\\D*)(.*)$", "$1");
String distance = input.replaceFirst("^(\\D*)(.*)$", "$2").trim();
Where the regular expression means
\\D matches non-digit
\\d matches digit
postfix * for 0 or more
. for any char
^ begin
$ end
() group numbered from 1
$1 group 1
I'm trying to implement a method which takes a string of text and a column
width and outputs the text, each line limited to the column width.
public void wrapText(String text, int width)
{
System.out.println(text);
}
For example, calling the method with the text:
Triometric creates unique end user monitoring products for high-value Web
applications, and offers unrivalled expertise in performance consulting.
with column width 20 would result in the following output:
Triometric creates
unique end user
monitoring products
for high-value Web
applications, and
offers unrivalled
expertise in
performance
consulting.
You can try something like this:
public static void wrapText(String text, int width) {
int count = 0;
for (String word : text.split("\\s+")) {
if (count + word.length() >= width) {
System.out.println();
count = 0;
}
System.out.print(word);
System.out.print(' ');
count += word.length() + 1;
}
}
Although there are still cases where the result of the method is not clear (for example, if the length of an individual word is greater than width). The code above will simply print such a word on its own line.
I would do it by splitting the String at whitelines and then printing word by word, like this:
public static void wrapText(String text, int width) throws Exception {
String[] words = text.split(" ");
int acsize = 0;
for (String word : words) {
if (word.length() > width) {
throw new Exception("Word longer than with!");
}
if (acsize + word.length() <= width) {
System.out.print(word + " ");
acsize += word.length() + 1;
} else {
System.out.println();
System.out.print(word + " ");
acsize = word.length() + 1;
}
}
}
The exception could be just removed, if you want to print words longer than width, like you said in you last comment.