4

Given a table-like data structure, e.g. an arraylist of arraylists (a 2D array, or some other iterable data structure), what would be the cleanest way to check the elements against specific rules?

For example given the following data:

[true, false, false, true]
[false, false, false, false]
[true, false, false, false]

How can I enforce that either of the three conditions are satisfied:

  • all elements in all rows are true
  • all elements in all rows are false
  • if a row contains mixed values, then only the first element can be true, otherwise the validation must fail.

For example, the data above must not pass validation because of a true value in position [0, 3].

UPDATE: Apparently one way to avoid looping in Java 8 is using myBoolArrayList.stream().allMatch(Boolean::booleanValue); and myBoolArrayList.stream().noneMatch(Boolean::booleanValue); — this would cover the first two conditions, not very clear yet about the third condition.

ccpizza
  • 28,968
  • 18
  • 162
  • 169

3 Answers3

2

Note that by looking at your three rules together, you don’t need to do individual checks:

A list can only contain all-true or all-false, but if either is valid, the combined rule is “all (boolean) values must be the same” which can be tested in a single expression like !booleanList.contains(!booleanList.get(0)). But there’s the third alternative:

if a row contains mixed values, then only the first element can be true, otherwise the validation must fail.

This basically says: if there is a false value, all but the first element must be false as well. This rule makes the other two obsolete:

  • if there is no false value, then all values are true
  • if there is a false value, the first element might be true, so all values being false is a special case of this rule
  • in other words, if there is a false value, the value of the first element is irrelevant

Therefore, we can short-cut the test by looking at any other element than the first one, e.g. at index 1 and selecting one rule to test, based on that value:

  • if the value is true, all values are required to be true
  • if the value is false, all but the first values are required to be false
  • as a corner case, if the list is smaller than two elements, there is no contradicting element, so the list is valid

So the entire condition can be expressed as

list.size()<2 ||
     list.get(1)? // has a TRUE at a position other than the first
     !list.contains(false): // all are TRUE
     !list.subList(1, list.size()).contains(true); // all except the first are FALSE

Regarding the Stream API,

  • !list.contains(x) can be expressed as list.stream().noneMatch(Predicate.isEqual(x)) and
  • !list.subList(1, list.size()).contains(x) can be expressed as list.stream().skip(1).noneMatch(Predicate.isEqual(x)),

but there’s no reason to use the Stream API here.

However, for validating a List of Lists, you can use the Stream API to check whether all sublists fulfill the condition:

static boolean validate(List<List<Boolean>> list2D) {
    return list2D.stream().allMatch(list-> list.size()<2 ||
        list.get(1)? !list.contains(false): !list.subList(1, list.size()).contains(true)
    );
}
Holger
  • 285,553
  • 42
  • 434
  • 765
1

You can use Guava libraries to check through the lists, for example:

[Updated: based on feedback... I don't think it's the nicest way, but it is a way of doing it].

import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import org.junit.Assert;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;

public class ValidationTest {

    @Test
    public void test() {
        // Here is the initial table, you can play with it.
        List<List<Boolean>> list = new ArrayList<List<Boolean>>() {{
            add(new ArrayList<Boolean>() {{
                add(false);
                add(false);
                add(false);
                add(false);
            }});
            add(new ArrayList<Boolean>() {{
                add(true);
                add(false);
                add(false);
                add(false);
            }});
            add(new ArrayList<Boolean>() {{
                add(true);
                add(true);
                add(true);
                add(true);
            }});
        }};
        boolean checkBooleanRule = this.checkBooleanRule(list);
        Assert.assertTrue("The table is invalid", checkBooleanRule);
    }

    private boolean checkRow(List<Boolean> row, final boolean value) {
        // If checking everything is true/false, let's just find if there is a False = !value
        return row.contains(!value);
    }

    private boolean checkBooleanRule(final List<List<Boolean>> inputList) {
        // Gets the row that brake the rule!
        Optional<List<Boolean>> invalidRow = Iterables.tryFind(inputList, new Predicate<List<Boolean>>() {
            @Override
            public boolean apply(List<Boolean> inputRow) {
                if (!checkRow(inputRow, true))
                    return false;
                if (!checkRow(inputRow, false))
                    return false;
                return (inputRow.get(0) ? checkRow(inputRow.subList(1, inputRow.size()), false) : true);
            }
        });
        // If Present, then, the Table is invalid! == There is a row with a false!
        return !invalidRow.isPresent();
    }
}
halfer
  • 19,824
  • 17
  • 99
  • 186
Marco Vargas
  • 1,232
  • 13
  • 31
  • Hi there. I am an editor here on Stack Overflow, and I often trim questions and answers into shape. I have noticed that you often put chatty phrases into your questions, and on this occasion you've rolled on of my edits back. We would rather that posts did not say "hope that helps" and "god bless" since they do not add anything to a particular post. Note that we're trying to be reference quality material, rather than a discussion forum. [This Meta discussion](http://meta.stackoverflow.com/q/320874) is relevant. – halfer Apr 21 '16 at 07:38
  • Agree! I just wanted the reason behind the change, actually thanks for your efforts, I'll avoid this kind of "chatty phrases" in the future, please feel free to update them back – Marco Vargas Apr 21 '16 at 10:59
  • Thanks Marco, will do. – halfer Apr 21 '16 at 11:59
0

This is one way to do it, using Java Lambda expressions. Reference is here What is the best way to filter a Java Collection?

Update thanks to comments

import java.util.ArrayList;

import java.util.stream.Stream;

public class Main {

    public static void main(String[] args) {

        ArrayList<Boolean> array = new ArrayList<Boolean>();
        array.add(true);
        array.add(true);
        array.add(true);
        array.add(false);

        //the simplest to read way 
        Stream<Boolean> arrayChecker = array.stream().filter(a -> a == true);

       //Checks if the stream filter is equal to the array size. If so then all true 
        System.out.println(arrayChecker.count() == array.size() );

        //the better way thanks to @ccpizza
        Stream<Boolean> arrayChecker = array.stream().filter(Boolean::booleanValue);

        //Even better way
        array.stream().allMatch(Boolean::booleanValue);

       //so basic I missed it. Thanks to @Holger
       System.out.println(! array.contains(false));
    }
}
Community
  • 1
  • 1
  • 1
    @Johathan: `a -> a == true` can be replaced with `Boolean::booleanValue`. – ccpizza Apr 06 '16 at 17:36
  • 1
    Instead of `array.stream().filter(predicate).count() == array.size()` you can simply use `array.stream().allMatch(predicate)`. This is not only shorter, but allows the stream implementation to stop at the first non-matching element. But still, `! array.contains(false)` is even simpler. – Holger Apr 06 '16 at 17:37
  • @Holger even better to know! thanks, will update the answer – Jonathan.Hickey Apr 06 '16 at 17:45
  • This doesn't answer the question at all of verifying either of the 3 conditions. – Tunaki Apr 06 '16 at 18:44
  • @Tunaki you are right, that this doesn't verify the 3 condition states. This answer was aimed to answer the "what would be the cleanest way to check the elements against specific rules?" My understanding was that the data above was an example situation and not the actual question.With my answer, I aimed to give enough information and line of thought to be applied to a generic problem, if it adapted to solve a real problem. Please view the comments in the original question – Jonathan.Hickey Apr 06 '16 at 19:51