aekit
aekit

Reputation: 405

How to remove repeating code in this solution?

I have this code which compresses characters in the given string and replaces repeated adjacent characters with their count.

Consider the following example:

Input:

aaabbccdsa

Expecting output:

a3b2c2dsa

My code is working properly but I think repeating if condition can be removed.

public class Solution {
    public static String getCompressedString(String str) {
        String result = "";
        char anch = str.charAt(0);
        int count = 0;

        for (int i = 0; i < str.length(); i++) {
            char ch = str.charAt(i);

            if (ch == anch) {
                count++;
            } else {
                if (count == 1) {           // from here
                    result += anch;
                } else {
                    result += anch + Integer.toString(count);                    
                }                         // to here

                anch = ch;
                count = 1;
            }

            if (i == str.length() - 1) {
                if (count == 1) {           // from here
                    result += anch;
                } else {
                    result += anch + Integer.toString(count);                    
                }                         // to here
            }
        }

        return result;
    }
}

In this solution code below is repeated two times

if (count == 1) {
    result += anch;
} else {
    result += anch + Integer.toString(count);                    
}

Please, note, I don't want to use a separate method for repeating logic.

Upvotes: 1

Views: 196

Answers (7)

user17233545
user17233545

Reputation:

Try this.

static final Pattern PAT = Pattern.compile("(.)\\1*");

static String getCompressedString(String str) {
    return PAT.matcher(str)
        .replaceAll(m -> m.group(1)
            + (m.group().length() == 1 ? "" : m.group().length()));
}

Test cases:

@Test
public void testGetCompressedString() {
    assertEquals("", getCompressedString(""));
    assertEquals("a", getCompressedString("a"));
    assertEquals("abc", getCompressedString("abc"));
    assertEquals("abc3", getCompressedString("abccc"));
    assertEquals("a3b2c2dsa", getCompressedString("aaabbccdsa"));
}

The regular expression "(.)\\1*" used here matches any sequence of identical characters. .replaceAll() takes a lambda expression as an argument, evaluates the lambda expression each time the pattern matches, and replaces the original string with the result. The lambda expression is passed a Matcher object containing the results of the match. Here we are receiving this object in the variable m. m.group() returns the entire matched substring, m.group(1) returns its first character.

If the input string is "aaabbccdsa", it will be processed as follows.

m.group(1)   m.group()  returned by lambda
 a            aaa        a3
 b            bb         b2
 c            cc         c2
 d            d          d
 s            s          s
 a            a          a

Upvotes: 0

Alexander Ivanchenko
Alexander Ivanchenko

Reputation: 28988

Here's a single-statement solution using Stream API and regular expressions:

public static final Pattern GROUP_OF_ONE_OR_MORE = Pattern.compile("(.)\\1*");

public static String getCompressedString(String str) {
    
    return GROUP_OF_ONE_OR_MORE.matcher(str).results()
        .map(MatchResult::group)
        .map(s -> s.charAt(0) + (s.length() == 1 ? "" : String.valueOf(s.length())))
        .collect(Collectors.joining());
}

main()

public static void main(String[] args) {
    System.out.println(getCompressedString("aaabbccdsa"));
    System.out.println(getCompressedString("awswwwhhhp"));
}

Output:

a3b2c2dsa   // "aaabbccdsa"
awsw3h3p    // "awswwwhhhp"

How does it work

A regular expression "(.)\\1*" is capturing a group (.) of identical characters of length 1 or greater. Where . - denotes any symbol, and \\1 is a back reference to the group.

Method Matcher.results() "returns a stream of match results for each subsequence of the input sequence that matches the pattern".

The only thing left is to evaluate the length of each group and transform it accordingly before collecting into the resulting String.

Links:

Upvotes: 1

Since, the body of the else and second if is the same, so we can merge them by updating the condition. The updated body of the function will be:

String result = "";
char anch = str.charAt(0);
int count = 0;

char ch = str.charAt(0); // declare ch outside the loop, and initialize to avoid error
for (int i = 0; i < str.length(); i++) {
    ch = str.charAt(i);

    if (ch == anch) {
        count++;
    }
    // check if the second condition is false, or if we are at the end of the string
    if (ch != anch || i == str.length() - 1) {
        if (count == 1) { // from here
            result += anch;
        } else {
            result += anch + Integer.toString(count);
        } // to here
        anch = ch;
        count = 1;
    }
}

// add the condition
// if count is greater than or
// if the last character added already to the result
if (count > 1 || (len < 2 || result.charAt(len - 2) != ch)) {
    result += ch;
}

return result;

Test Cases:

I have tested the solution on the following inputs:

aaabbccdsa -> a3b2c2dsa
aaab -> a3b
aaa -> a3
ab -> ab
aabbc -> a2b2c

Optional

If you want to make it shorter, you can update these 2 conditions.

if (count == 1) { // from here
    result += anch;
} else {
    result += anch + Integer.toString(count);
} // to here

as

result += anch;
if (count != 1) { // from here
    result += count;// no need to convert (implicit conversion)
} // to here

Upvotes: 1

Cheng Thao
Cheng Thao

Reputation: 1493

You could do away with the if statements.

   public static String getCompressedString(String str) {
        char[] a = str.toCharArray();
        StringBuilder sb = new StringBuilder();
        for(int i=0,j=0; i<a.length; i=j){
           for(j=i+1;j < a.length && a[i] == a[j]; j++);
           sb.append(a[i]).append(j-i==1?"":j-i);
        }
        return sb.toString();
   }
}

Upvotes: 2

iamgirdhar
iamgirdhar

Reputation: 1155

You can use this approach as explained below:

Code:

public class Test {
    public static void main(String[] args) {
        String s = "aaabbccdsaccbbaaadsa";
        char[] strArray = s.toCharArray();
        char ch0 = strArray[0];
        int counter = 0;
        StringBuilder sb = new StringBuilder();
        for(int i=0;i<strArray.length;i++){
            if(ch0 == strArray[i]){//check for consecutive characters and increment the counter
                counter++;
            } else { // when character changes while iterating
                sb.append(ch0 + "" + (counter > 1 ? counter : ""));
                counter = 1; // reset the counter to 1
                ch0 = strArray[i]; // reset the ch0 with the current character
            }
            if(i == strArray.length-1){// case for last element of the string
                sb.append(ch0 + "" + (counter > 1 ? counter : ""));
            }
        }
        System.out.println(sb);
    }
}

Sample Input/Output:

Input:: aaabbccdsaccbbaaadsa
Output:: a3b2c2dsac2b2a3dsa

Input:: abcdaaaaa
Output:: abcda5

Upvotes: 1

Nibras Shami
Nibras Shami

Reputation: 302

You can do something like this:

public static String getCompressedString(String str) {
        String result = "";
        int count = 1;
        for (int i = 0; i < str.length(); i++) {
            if (i + 1 < str.length() && str.charAt(i) == str.charAt(i + 1)) {
                count++;
            } else {
                if (count == 1) {
                    result += str.charAt(i);
                } else {
                    result += str.charAt(i) + "" + count;
                    count = 1;
                }
            }
        }
        return result;
    }

I got rid of the repeated code, and it do as intended.

Upvotes: 1

Gurkirat Singh Guliani
Gurkirat Singh Guliani

Reputation: 1015

You can use a function which has the following 3 parameters : result, anch, count .

something of this sort:

  private static String extractedFunction(String result,int count, char anch) {
               
                return count ==1 ? (result + anch) : (result +anch+Integer.toString(count) );
            }

make a function call from those two points like this :

result = extractedFunction(result,count,anch);
                  

Upvotes: 0

Related Questions