2

Not sure how to word this algorithm question, actually. I have toggle buttons in an android app, each corresponding to a numbered channel. Channels are (1,n) but the toggle button ids are some unknown, unsequential (to the buttons) integer. I need to collect the channel numbers and build a command string. My 1995-based Java skills have given me this:

String ch;
int i = 1;
for (ToggleButton toggle : toggles) {
    if (toggle.isChecked()) {
        ch = String.format("+%d", i+1);
        channels = channels.concat(ch);
    }
} 

If toggle buttons 1,2,4,5,6,7,11,13,21,22,23,24,25 are checked, this code snippet successfully gives me the string "+1+2+4+5+6+7+11+13+21+22+23+24+25"

However, what I would more like to do is have the string "+1/2, +4/7, +11, +13, +21/25"

I wonder if there's an easier way to do this than multiple if statements:

String ch;
int it = 0;
int last = 1;
for (ToggleButton toggle : toggles ) {
    if (toggle.isChecked()) {
    if (it == last + 1) {
            // somehow continue or note that we're only adding the "/"
        } else {
            // If we're not the next one, then build the last string
            // of "n/last" and then add ", +" to start building next string
        }
    }
    last++;
}

That seems like a bit of brute force type of algorithm, so I don't know if there's a more elegant solution that Java might have in it's bag of tricks (Or, more likely, that I should have)

Thanks.

3
  • I think what you mean is 1-2, 4-7, ... Also you said 22/25, I think you mean 21-25. There is enough ambiguity that I'm afraid to change your post in case I'm guessing incorrectly. Commented Aug 10, 2010 at 17:09
  • Bill, Thanks. 21/25 is correct. However, I actually do mean 1/2, 4/7. However, since "/" is just a single character string as is "-", it doesn't really matter. This is a weird telnet command set that I'm working with, and the commands for multiple channels uses a slash for some reason. Commented Aug 10, 2010 at 17:26
  • I just mean that on here it makes more sense intuitively when asking the question. You could do the translation after the fact--or pass the separator as a parameter to the constructor when you instantiate the helper class in my solution :) Commented Aug 10, 2010 at 17:34

2 Answers 2

1

On java.util.BitSet

I'd use java.util.BitSet both for the representation and the span search algorithm. Here's the basic idea (see on ideone.com):

    import java.util.*;
    //...

    BitSet bs = new BitSet();
    int[] onBits = new int[] { 1,2,4,5,6,7,11,13,21,22,23,24,25 };
    for (int onBit : onBits) {
        bs.set(onBit);
    }
    System.out.println(bs);
    // {1, 2, 4, 5, 6, 7, 11, 13, 21, 22, 23, 24, 25}

    StringBuilder sb = new StringBuilder();
    for (int begin, end = -1; (begin = bs.nextSetBit(end + 1)) != -1; ) {
        end = bs.nextClearBit(begin) - 1;
        if (sb.length() > 0) sb.append(", ");
        sb.append(
            (begin == end)
                ? String.format("+%d", begin)
                : String.format("+%d/%d", begin, end)
        );
    }
    System.out.println(sb);
    // +1/2, +4/7, +11, +13, +21/25

The nextSetBit and nextClearBit methods are really handy in finding the spans.


On StringBuilder instead of String with +=/concat

The StringBuilder joining algorithm is a standard one. Here it is when refactored out:

    StringBuilder sb = new StringBuilder();
    for (Element e : elements) {
        if (sb.length() > 0) sb.append(SEPARATOR);
        sb.append(e);
    }

You should use a StringBuilder or StringBuffer instead of String with +=/concat for building long strings.

Sign up to request clarification or add additional context in comments.

3 Comments

By the way, BitSet has a flip/"toggle" method, plus set-like operations like or (union) and and (intersection).
You should use StringBuffer only if you require thread-safety or you're targeting a version of Java that doesn't support it (pre-1.5 or some versions of MIDP).
When using StringBuilder, you have to initialize the capacity first, or else you are not gaining any performance here compared to String "+" concat.
0

Yours won't work, there is an end-case problem where you will have to duplicate your code to clean up after a range ends (try it on your dataset)

I know you were looking for short/terse, but my preference is to make the business logic code shorter and cleaner by doing something like this:

Counter counter=new Counter();

for(int i=1;i<=toggles.length;i++)
    if(toggles.get(i).isChecked())
        counter.append(i);

System.out.println(counter.getResult());

so that portion of your code is shorter, but you need a new class. This class is reusable, any time you want to re-create the same kind of range list, you've got it!. Both sections of code should be more understandable because each is only doing it's own job. It's a little longer, but honestly I'm not a fan of terse in any way, shape or form. As long as you are DRY, the more explicit the better.

PS. I'm going brace-free today.

So:

public class counter
    int a=-1;
    int b=-1;
    String result="";

    public append(int i)
        if(a == -1)
            a=i;
            b=i;
        else if(b == i-1)
            b++;
        else
            finished();

    public finished()
        if(result.length != 0 && a != -1)
            result+="+";
        if(a != -1)
            result.append(a);
            if(a != b)
                result.append("/"+b);
        a=-1;
        b=-1;

    public String getResult()
        finished();
        return result;

1 Comment

This had a few bugs, probably still does. Be sure check back--I've updated it at least 3 times now, but I think it'll work at this point.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.