1

I have a String for an editor in a 2D platform game, where the user clicks on a 16x60 rectangle board to create a custom board. I loop through these rectangles and get a String that I'd like to trim down. The x's are blocks, and a's are empty space. I need this to draw the level.

String s = "x1x1x1x1a1a1x1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1";

I want to trim this to x4a2x1a54. Basically adding up the x's and a's that show up right after each other.

How do I do this?

Ryan Sangha
  • 442
  • 6
  • 17
  • 4
    Loop through the characters, remember the last letter you read and the current count; if the current letter is the same as the last one, increase the count, otherwise output the last letter and the count, and reset both. – James_D May 06 '18 at 14:03

5 Answers5

1

Loop though the characters and store the last alphabetic character. For each of those chars parse the number that follows it. It the alphabetic char is the same as the last one you can add this number to the count, otherwise write the old results to the output:

public static String compress(String input) {
    if (input == null || input.isEmpty()) {
        return input;
    }
    char type = ' ';
    int count = 0;
    StringBuilder output = new StringBuilder();
    int index = 0;
    final int length = input.length();
    while (index < length) {
        char elementType = input.charAt(index);

        // parse number
        int elementCount = 0;
        char c;
        for (index++; index < length && Character.isDigit(c = input.charAt(index)); index++) {
            elementCount = 10 * elementCount + (c - '0');
        }

        if (elementType == type) {
            count += elementCount;
        } else {
            // finish counting last type
            output.append(type).append(count);
            type = elementType;
            count = elementCount;
        }
    }
    output.delete(0, 2); // remove substring added for the initial count/type
    output.append(type).append(count); // append last entry
    return output.toString();
}

This allows you to output numbers different to 1, e.g.

compress("a22a20x9")
fabian
  • 80,457
  • 12
  • 86
  • 114
0

Just to let you know, stackoverflow is not a forum to get code but to help you with your code and point out errors or give suggestions.

I rather dislike a demand but since this problem catched me a bit here is my version of it. Unfortunatelly I wasn't able to use only Streams and had to add an Arraylist to it (maybe with the reduce and collect methode of streams it is possible in one line) but it works as intended.

    String s = "x1x1x1x1a1a1x1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1";
    ArrayList<String[]> map = new ArrayList<>();
    for(String c:s.split("(?<=\\G..)")){
        if(map.isEmpty()||!map.get(map.size()-1)[0].equals(c.substring(0,1))){
            map.add(new String[]{c.substring(0,1),c.substring(1)});
        } else{
            map.get(map.size()-1)[1]=(Integer.parseInt(map.get(map.size()-1)[1])+Integer.parseInt(c.substring(1)))+"";
        }
    }
    StringBuilder sb = new StringBuilder();
    map.forEach(x-> sb.append(x[0]).append(x[1]));
    System.out.println(sb.toString());

EDIT: I changed the code so that other numbers than 1 are also added to the String and I did not use Streams for now on but for-loops

Scorix
  • 487
  • 6
  • 20
  • You should probably also note the assumptions made in this code, since the specification was not really clear, e.g. you assume that the input string only has `1` as the value, etc. – James_D May 06 '18 at 14:41
  • 1
    Just for the sake of using streams you a) created a list that would otherwise not be required b) used an array that is not suitable to store both of the types of you want to store in it and store a `Integer`s in string format, parse them from string and convert them back to string just to increment them... – fabian May 06 '18 at 14:44
  • I used the stream to surpass a forloop, I changed the code so that there would be no Stream and @James_D I added your suggestion thank you for that – Scorix May 06 '18 at 15:00
0

The first thing that I would suggest is breaking this string down into easier to manage sections like this:

    String s = "x1x1x1x1a1a1x1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1";
    String[] splitS = s.split("1");
    //Contents of the array are: [x, x, x, x, a, a, x, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a]
    //We can now loop through each string in the string array and count how many we have of what
    ArrayList<String> values = new ArrayList<String>();
    int start = 0, end = 0, grandCounter = 0;
    String current = splitS[0];
    for(int i = 1; i < splitS.length-1; i++){
        if(!splitS[i].equals("x") && current.equals("x")){
            end = i;
            values.add("x" + (end - start));
            grandCounter += (end-start);
            start = end;
            end = 0;
            current = "a";
        } else if(!splitS[i].equals("a") && current.equals("a")){
            end = i;
            values.add("a" + (end - start));
            start = end;
            end = 0;
            current = "x";
            grandCounter += (end-start);
        }
    }
    values.add( "a" + (splitS.length-grandCounter) + "");
    System.out.println(values.toString().replace("[", "").replace(",", "").replace("]", "").replace(" ", ""));

The above code exactly returned the following when told to print in console: x4a2x1a58

Please let me know if you have any further questions or issues with your work!

Kwright02
  • 777
  • 3
  • 21
  • Lots of this seems redundant. E.g. `String current = "x" ; if (splitS[0].equals("a")) { current ="a" ; }`: why not just do `String current = splitS[0];`? The `continue` statements are redundant too. – James_D May 06 '18 at 15:24
  • I coded this pretty quickly so I apologize for the redundancy, i'll go fix that now. – Kwright02 May 06 '18 at 15:25
  • Also, if I read this correctly, you assume that the last one is `a`? That seems like an unreasonable assumption. – James_D May 06 '18 at 15:27
  • True, I do have that, but in the event that the last one is NOT a then you'd just have an a0 at the end so If the OP didn't want that he could just remove it with the other .replace – Kwright02 May 06 '18 at 15:28
  • "you'd just have an a0 at the end". Really? Why? – James_D May 06 '18 at 17:03
-1

Here's a simpler method than the ones already posted:

 public static String customTrim(String s){

String res = "";
String last = "";
int count = 0;

for (int i = 0; i < s.length() - 1; i+=2){

  String curr = "" + s.charAt(i) + s.charAt(i+1);

  if(!curr.equals(last)){
      count = 1;
      res += "" + s.charAt(i) + count;
  }

  else{
    int subLength = ("" + count).length();
    count++;
    res = res.substring(0, res.length()- subLength) + count;
  }

  last = curr;

}
return res;
}
ninesalt
  • 4,054
  • 5
  • 35
  • 75
  • Never build strings by concatenation in a loop. – James_D May 06 '18 at 15:13
  • @James_D why not? – ninesalt May 06 '18 at 15:29
  • Because the performance is horrible. On *every* modification of the string, you create a new string object and copy the characters from the old string to the new string. You basically do O(n^2) character copies, where n is the length of the resulting string. See https://stackoverflow.com/q/5234147/ (for exampe) – James_D May 06 '18 at 15:32
-1

You could do it the way fabian posted, which works just fine. However, I figured it out myself by just having three simple if's.

    StringBuilder sb = new StringBuilder();

    List<String> values = new ArrayList<>();

    String s = "x1x1x1x1a1a1x1x1a1a1a1";
    String[] splitString = s.split("1");
    // "xxxxaaxxaaa"
    // to x4a2x2a3

    String current = splitString[0];
    int occ = 0;

    for(int i = 0; i < splitString.length;i++){

        if(!splitString[i].equals(current)){
            values.add(current+occ);
            occ = 0;
            current = splitString[i];
        }

        if(splitString[i].equals(current)){
            occ++;
        }

        if(i == splitString.length-1){
            values.add(current+occ);
        }

    }

    for (String str: values
         ) {
        sb.append(str);
    }

    System.out.println(sb);
}
Ryan Sangha
  • 442
  • 6
  • 17
  • This makes all sorts of assumptions that are not included in the actual question. – James_D May 06 '18 at 21:06
  • What do you mean? This will still work if you have different characters like f or g which I plan to implement in my game. – Ryan Sangha May 06 '18 at 21:17
  • You're assuming all the integers are `1`, which is not stated in the question. If that's true, then it would seem that having them there in the first place is redundant. – James_D May 06 '18 at 21:28
  • Ohh, yeah. You're right. It should have been removed. – Ryan Sangha May 06 '18 at 21:32