Choice between less duplicate code or efficient but duplicate code - java

I'm trying the exercises in CodingBat.
Java > String-1 > seeColor:
Given a string, if the string begins with "red" or "blue" return that color string, otherwise return the empty string.
My running code is:
public String seeColor(String str) {
int len = str.length();
if (len >= 3 && str.substring(0, 3).equals("red")) {
return str.substring(0, 3);
} else if (len >= 4 && str.substring(0, 4).equals("blue")) {
return str.substring(0, 4);
}
return "";
}
But I found this other answer all over the web:
public String seeColor(String str) {
int len = str.length();
if(len >= 4) {
if(str.substring(0, 4).equals("blue"))
return "blue";
else if(str.substring(0, 3).equals("red"))
return "red";
else
return "";
}
else if(len == 3) {
if(str.substring(0, 3).equals("red"))
return "red";
else
return "";
}
else
return "";
}
My solution has less duplicate code (or it's what I think).
But the other solution looks more efficient. It starts from large Strings, and evaluating only the strings with length of 3 characters at the end. Probably this last solution is more able to make easy changes in the future.
I would like to read someone's opinion. Thanks.
Edited: I add an apologize, I haven't formatted my code appropriately at first as suggested by #MikeDunlavey.

I wouldn't do either. I wouldn't assume the second one is more efficient, though it is more complicated. Both create objects needlessly.
public String seeColor(String str) {
return str.startsWith("red") ? "red" :
str.startsWith("blue")? "blue" : "";
}
Every time you call substring this creates two objects which is really expensive for what this function is doing.

Related

Finding the last character before a given character inside a Java String

Say I have the following string:
String string = "122045b5423";
In Java, what would be the most efficient way in finding the last 2 before the b?
I know I can split the string and then use lastIndexOf() method from String class, but
is there a more efficient way with less creating of variables. Can there be a method in the StringBuilder class that will allow us to do this?
If you are looking for a more compact solution, how about regex?
// A 2, followed by arbitrary chars that are not a 2 and finally a b
Pattern pattern = Pattern.compile("(2)[^2]*b");
Matcher matcher = pattern.matcher(string);
if (matcher.find()) {
System.out.print("Start index: " + matcher.start());
System.out.print(" End index: " + matcher.end());
System.out.println(" Found: " + matcher.group());
}
Have not tested it, but something similar should work
I think the simplest (with almost zero memory-overhead) is to simply scan the string yourself:
int findLastCharBeforeChar(final String string, final char anchor, final char needle) {
int i = string.length() - 1;
while (i >= 0 && string.charAt(i) != anchor) {
--i;
}
while (i >= 0) {
if (string.charAt(i) == needle) return i;
--i;
}
return i;
}
If you want to make that a bit shorter (but likely minimally slower and definitely harder to read):
int findLastCharBeforeChar(final String string, final char anchor, final char needle) {
char target = anchor;
while (i >= 0) {
final char ch = string.charAt(i);
if (ch == target) target = needle;
if (target == needle && ch == target) return i;
--i;
}
return i;
}
Not what was asked (most efficient), but followed up in the comments for the "shortest" solution, there you go (note that this is far from efficient, and depending on where you call it, this could be bad):
string.split('b')[0].lastIndexOf('2');
You didn't specify in your OP what should happen if 'b' is not part of the input string. Should the result be -1? (will be with my first implementation) or should the method then just return the index of the last '2' in the string (the string split solution)? Changing the method to handle this case as well is trivial, just check if the first loop terminated at -1 and reset the index to the string's last index.
But this is somewhat moot. You put the 9 lines of code in a method, write proper unit tests for it and then call your new method. Calling the new method is: a) a one-liner b) efficient c) likely to be inlined by the JVM
Look at the method substring of String class (or subSequence). That should give you what you need.
the code Should be something like this
String result = null;
int index = myString.indexOf("b");
if(index > -1) {
if(index >= 2) {
result = myString.substring(index - 2, index);
} else {
result = myString.substring(0, index);
}
}

My code passes all of the given tests for CodingBat but doesn't pass "other tests". I'm not sure what my problem is

I'm working on CodingBat for class and can't figure out why my code isn't working. It passes all of the tests that it lists out but then doesn't pass the "other tests". If you could help me figure out what's wrong that would be really helpful. Here is the problem:
Given a string, count the number of words ending in 'y' or 'z' -- so the 'y' in "heavy" and the 'z' in "fez" count, but not the 'y' in "yellow" (not case sensitive). We'll say that a y or z is at the end of a word if there is not an alphabetic letter immediately following it. (Note: Character.isLetter(char) tests if a char is an alphabetic letter.)
Here are the tests that it listed and I passed.
countYZ("fez day")
countYZ("day fez")
countYZ("day fyyyz")
countYZ("day yak")
countYZ("day:yak")
countYZ("!!day--yaz!!")
countYZ("yak zak")
countYZ("DAY abc XYZ")
countYZ("aaz yyz my")
countYZ("y2bz")
countYZ("zxyx")
Here is the code I have so far. (I know I could have done it cleaner by converting to lower case but I only realized that afterwards.)
public int countYZ(String str) {
int count = 0;
str = str.toLowerCase();
for (int i=0; i<str.length()-1; i++) {
if (!Character.isLetter(str.charAt(i)) && i>0 && (str.substring(i-1,i).equals("y") || str.substring(i-1,i).equals("z")))
{ count++; }
}
if (str.endsWith("y") || str.endsWith("z")) { count++; }
return count;
}
The problem: The way you have set up your logic, your loop looks up until one character before the end of the String and then you check the last character with your if statement. However, your loop only effectively checks the char before i, so you are not checking a char. A String that this will fail on is:
"y z "
(Notice the extra space at the end. It can be any other non letter character, which is defined as the ending of the prior word)
This String should return 2, but returns 1. The easy fix is to change your loop to:
for (int i=0; i<str.length(); i++)
Perhaps a better approach:
Right now you have a mess of boolean conditions to see if the character before is a non alphabetic, if the character at i is alphabetic and so on. If I were you I would simply use toLower(), and then split on any non alphabetic characters. Then for each String in the split array, use the endsWith function to easily check if it ends with z or y. Here's one possible solution:
public int countYZ(String str) {
if(str == null) return 0;
str = str.toLowerCase();
int count = 0;
for(String s : str.split("[^a-z]")) {
if(s.endsWith("y") || s.endsWith("z")) {
count++;
}
}
return count;
}
Or java 8+ you can simply do:
public int countYZ(String str) {
if(str == null) return 0;
return (int)Arrays.stream(str.toLowerCase().split("[^a-z]"))
.filter(e -> e.endsWith("y") || e.endsWith("z"))
.count();
}
Which solves all the test case:
Link to problem
Cleaning up your code will make it be easier to read and to debug.
Don't Repeat Yourself! Anytime you find yourself writing the same piece of code you are introducing potential bugs due to simple typing errors. Instead, you can move all the checks into a single method, and use equalsIgnoreCase to check if a given character matches Y or Z:
public static boolean isYOrZ(String s) {
if(s.equalsIgnoreCase("y") || s.equalsIgnoreCase("z")) return true;
else return false;
}
Right now, your code fails for null strings and for empty strings, so add a check before doing any processing:
int count = 0;
if(str == null || str.length() == 0) //check null first or you will get NPE!
return count;
Finally, you can update your code with the new helper method you created. If you understand how regex works, it's easier to use String.split() to process your words:
public static int countYZ(String str) {
int count = 0;
if(str == null || str.length() == 0) {
return count;
}
String[] words = str.split("[^A-z]"); //"not an alphabetic letter immediately following it"
for(String word : words) {
if(word.length() > 0) { //sanity check
if(isYOrZ(word.substring(word.length()-1))) {
++count;
}
}
}
return count;
}
Now you can test, write as many weird test cases that you can think of. For example, a huge string of spaces, an empty string, etc. Here are a few I tried:
System.out.println(countYZ("heavY yummy TASTY yellow zed buzz"));
System.out.println(countYZ(""));
System.out.println(countYZ(null));
System.out.println(countYZ("fiZz yay"));
System.out.println(countYZ("zzzZZza yyy"));
System.out.println(countYZ("z"));
System.out.println(countYZ(" "));
System.out.println(countYZ("heavy&testy#!##BuzZ")); //3
Which gives:
4
0
0
2
1
1
0
3

How to detect middle 3 characters from an odd number string that is inputted using a scanner? [duplicate]

I'm trying to take the last three chracters of any string and save it as another String variable. I'm having some tough time with my thought process.
String word = "onetwotwoone"
int length = word.length();
String new_word = id.getChars(length-3, length, buffer, index);
I don't know how to use the getChars method when it comes to buffer or index. Eclipse is making me have those in there. Any suggestions?
Why not just String substr = word.substring(word.length() - 3)?
Update
Please make sure you check that the String is at least 3 characters long before calling substring():
if (word.length() == 3) {
return word;
} else if (word.length() > 3) {
return word.substring(word.length() - 3);
} else {
// whatever is appropriate in this case
throw new IllegalArgumentException("word has fewer than 3 characters!");
}
I would consider right method from StringUtils class from Apache Commons Lang:
http://commons.apache.org/proper/commons-lang/apidocs/org/apache/commons/lang3/StringUtils.html#right(java.lang.String,%20int)
It is safe. You will not get NullPointerException or StringIndexOutOfBoundsException.
Example usage:
StringUtils.right("abcdef", 3)
You can find more examples under the above link.
Here's some terse code that does the job using regex:
String last3 = str.replaceAll(".*?(.?.?.?)?$", "$1");
This code returns up to 3; if there are less than 3 it just returns the string.
This is how to do it safely without regex in one line:
String last3 = str == null || str.length() < 3 ?
str : str.substring(str.length() - 3);
By "safely", I mean without throwing an exception if the string is nulls or shorter than 3 characters (all the other answers are not "safe").
The above code is identical in effect to this code, if you prefer a more verbose, but potentially easier-to-read form:
String last3;
if (str == null || str.length() < 3) {
last3 = str;
} else {
last3 = str.substring(str.length() - 3);
}
String newString = originalString.substring(originalString.length()-3);
public String getLastThree(String myString) {
if(myString.length() > 3)
return myString.substring(myString.length()-3);
else
return myString;
}
If you want the String composed of the last three characters, you can use substring(int):
String new_word = word.substring(word.length() - 3);
If you actually want them as a character array, you should write
char[] buffer = new char[3];
int length = word.length();
word.getChars(length - 3, length, buffer, 0);
The first two arguments to getChars denote the portion of the string you want to extract. The third argument is the array into which that portion will be put. And the last argument gives the position in the buffer where the operation starts.
If the string has less than three characters, you'll get an exception in either of the above cases, so you might want to check for that.
Here is a method I use to get the last xx of a string:
public static String takeLast(String value, int count) {
if (value == null || value.trim().length() == 0 || count < 1) {
return "";
}
if (value.length() > count) {
return value.substring(value.length() - count);
} else {
return value;
}
}
Then use it like so:
String testStr = "this is a test string";
String last1 = takeLast(testStr, 1); //Output: g
String last4 = takeLast(testStr, 4); //Output: ring
This method would be helpful :
String rightPart(String text,int length)
{
if (text.length()<length) return text;
String raw = "";
for (int i = 1; i <= length; i++) {
raw += text.toCharArray()[text.length()-i];
}
return new StringBuilder(raw).reverse().toString();
}
The getChars string method does not return a value, instead it dumps its result into your buffer (or destination) array. The index parameter describes the start offset in your destination array.
Try this link for a more verbose description of the getChars method.
I agree with the others on this, I think substring would be a better way to handle what you're trying to accomplish.
You can use a substring
String word = "onetwotwoone"
int lenght = word.length(); //Note this should be function.
String numbers = word.substring(word.length() - 3);
Alternative way for "insufficient string length or null" save:
String numbers = defaultValue();
try{
numbers = word.substring(word.length() - 3);
} catch(Exception e) {
System.out.println("Insufficient String length");
}
This method will return the x amount of characters from the end.
public static String lastXChars(String v, int x) {
return v.length() <= x ? v : v.substring(v.length() - x);
}
//usage
System.out.println(lastXChars("stackoverflow", 4)); // flow

Returning a string minus a specific character between specific characters

I am going through the Java CodeBat exercises. Here is the one I am stuck on:
Look for patterns like "zip" and "zap" in the string -- length-3, starting with 'z' and ending with 'p'. Return a string where for all such words, the middle letter is gone, so "zipXzap" yields "zpXzp".
Here is my code:
public String zipZap(String str){
String s = ""; //Initialising return string
String diff = " " + str + " "; //Ensuring no out of bounds exceptions occur
for (int i = 1; i < diff.length()-1; i++) {
if (diff.charAt(i-1) != 'z' &&
diff.charAt(i+1) != 'p') {
s += diff.charAt(i);
}
}
return s;
}
This is successful for a few of them but not for others. It seems like the && operator is acting like a || for some of the example strings; that is to say, many of the characters I want to keep are not being kept. I'm not sure how I would go about fixing it.
A nudge in the right direction if you please! I just need a hint!
Actually it is the other way around. You should do:
if (diff.charAt(i-1) != 'z' || diff.charAt(i+1) != 'p') {
s += diff.charAt(i);
}
Which is equivalent to:
if (!(diff.charAt(i-1) == 'z' && diff.charAt(i+1) == 'p')) {
s += diff.charAt(i);
}
This sounds like the perfect use of a regular expression.
The regex "z.p" will match any three letter token starting with a z, having any character in the middle, and ending in p. If you require it to be a letter you could use "z[a-zA-Z]p" instead.
So you end up with
public String zipZap(String str) {
return str.replaceAll("z[a-zA-Z]p", "zp");
}
This passes all the tests, by the way.
You could make the argument that this question is about raw string manipulation, but I would argue that that makes this an even better lesson: applying regexes appropriately is a massively useful skill to have!
public String zipZap(String str) {
//If bigger than 3, because obviously without 3 variables we just return the string.
if (str.length() >= 3)
{
//Create a variable to return at the end.
String ret = "";
//This is a cheat I worked on to get the ending to work easier.
//I noticed that it wouldn't add at the end, so I fixed it using this cheat.
int minusAmt = 2;
//The minus amount starts with 2, but can be changed to 0 when there is no instance of z-p.
for (int i = 0; i < str.length() - minusAmt; i++)
{
//I thought this was a genius solution, so I suprised myself.
if (str.charAt(i) == 'z' && str.charAt(i+2) == 'p')
{
//Add "zp" to the return string
ret = ret + "zp";
//As long as z-p occurs, we keep the minus amount at 2.
minusAmt = 2;
//Increment to skip over z-p.
i += 2;
}
//If it isn't z-p, we do this.
else
{
//Add the character
ret = ret + str.charAt(i);
//Make the minus amount 0, so that we can get the rest of the chars.
minusAmt = 0;
}
}
//return the string.
return ret;
}
//If it was less than 3 chars, we return the string.
else
{
return str;
}
}

Workaround for StringIndexOutOfBoundsException while testing for certain characters

I'm making an if conditional to test to see if a string given contains 'me' at the end of it.
Given Return
----- ------
Lame True
Meant False
Come True
etc
Right now my code is working perfectly if the string is greater than 2 characters long.
public boolean containsLy(String input) {
String ly = "ly";
String lastString = input.substring(input.length() - 2);
if (input.length() < 2) {
return false;
}else if (lastString.equals(ly)) {
return true;
}else
return false;
}
But whenever the string has 2 characters or less I get this error:
StringIndexOutOfBoundsException
This is obviously because of a negative number, but I can't think of a workaround for this.
If you want to return false if the length of the input is inferior than 2, you can made the check before trying to perform the substring operation on your input.
public boolean containsLy(String input) {
if (input == null || input.length() < 2) {
return false;
}
else {
String ly = "ly";
String lastString = input.substring(input.length() - 2);
if (lastString.equals(ly)) {
return true;
}
else {
return false;
}
}
}
Or more simple :
public boolean containsLy(String input) {
if (input == null || input.length() < 2) {
return false;
}
else {
String ly = "ly";
String lastString = input.substring(input.length() - 2);
return lastString.equals(ly);
}
}
Or, to get rid of all the if/else variables stuff (thanks to #Ingo) :
public boolean containsLy(String input) {
return input != null
&& input.length() >= 2
&& input.substring(input.length() - 2).equals("ly");
}
How about checking like this
boolean endsWithMe = "myteststring".endsWith("me");
If you are doing it for practice then:
Pseudo code:
if length of the given string < 2 then return false
else substring from index length - 2 to length equals "me"
Don't reinvent the wheel. Just use String#endsWith()
By the way, it is remarkable that you already found out why it didn't work the way you did it. But still, you first try to take the substring, and only then you test if it is even possible. Why?
workaround is simple.. check for the string length before performing substring of it or use the indexOf("me") to get the index and use the length to see if it is the in the position length-2.

Categories