How do I run logical 'xnor' across a column in a multi-dimensional array (linq or simple algorithm)?

262 views Asked by At

Currently, I have an array of possibilities, and I am looking to calculate the facts (logical XNOR) based on that list of possibilities.

var possibilities = new[] {
    new[] {0, 1, 1, 1, 1, 1, 1, 1},
    new[] {1, 1, 1, 1, 1, 1, 1, 0}
}

I am trying to convert that into a boolean array of XNOR (all values equal)

Expected Result:

[ 0, 1, 1, 1, 1, 1, 1, 0 ]

The number of possibilities is 1 .. n. Another example would be:

var possibilities = new[] {
    new[] {1, 1, 0, 1, 1, 0, 0, 0},
    new[] {1, 1, 0, 1, 1, 0, 0, 1},
    new[] {0, 1, 1, 0, 1, 1, 0, 1}
}

Where result would be:

[ 0, 1, 0, 0, 1, 0, 1, 0 ]

My original approach was to start building nested loops and iterating over each possibility, grabbing the index and comparing it, however that seemed very 'hackish', and I believe there is a simpler (better) way of handling it via LINQ (however I do not know LINQ well enough to write it) or an algorithm that doesn't require ugly nested loops.

Edit #1: Incorrect 'logic' term used

As mentioned in the answer below, the correct logic was not in fact AND rather XNOR (where all the columns are equal). AND would, in fact, produce 0 where all the columns were 0 when I was (as noted above), was really looking for a result of 1 (or true) if all the values were equal.

4

There are 4 answers

4
Ashkan Mobayen Khiabani On BEST ANSWER
int size = possibilities[0].Length;
int i = 0;
var a = possibilities.SelectMany(x => x).GroupBy(x => i++ % size)
              .Select(x => x.Any(z => z == 0) ? 0 : 1);

And Using loops:

int j = 0, size = possibilities[0].Length;
int[] result = new int[size];
for (int i = 0; i < size; i++)
{ 
     for (j = 0; j < possibilities.Length; j++)
         if (possibilities[j][i] == 0) { result[i] = 0; break; }
     if (j == possibilities.Length) result [i] = 1;
}

XNOR:

int i = 0;
var a = possibilities.SelectMany(x => x).GroupBy(x => i++ % size)
            .Select(x => x.All(z => z == 0) || x.All(z => z == 1) ? 1 : 0);

XNOR with loops:

int size = possibilities[0].Length;
int[] result = new int[size];
for (int i = 0; i < size; i++)
{ 
     int q = possibilities[0][i];
     for (j = 1; j < possibilities.Length; j++)
         if (possibilities[j][i] != q) { result[i] = 0; break; }
     if (j == possibilities.Length) result[i] = 1;
}

Live Demo

6
Clayton Harbich On

You are looking for the zip extension. But you need to use aggregate to handle more than two. The only discrepancy is the three zeros you have a result of 1. The and operator (&) will be zero.

    static void Main(string[] args)
    {
        var possibilities = new[]
        {
            new[] {0, 1, 1, 1, 1, 1, 1, 1},
            new[] {1, 1, 1, 1, 1, 1, 1, 0}
        };
        //[ 0, 1, 1, 1, 1, 1, 1, 0 ]
        var result = possibilities.Aggregate((f, s) => f.Zip(s, (fi, si) => fi & si).ToArray());
        var possibilities2 = new[]
        {
            new[] {1, 1, 0, 1, 1, 0, 0, 0},
            new[] {1, 1, 0, 1, 1, 0, 0, 1},
            new[] {0, 1, 1, 0, 1, 1, 0, 1}
        };
        //[ 0, 1, 0, 0, 1, 0, 1, 0 ]
        var result2 = possibilities2.Aggregate((f, s) => f.Zip(s, (fi, si) => fi & si).ToArray());

        Console.ReadLine();
    }
2
Jack On

The most simple implementation here is to say something like:

for(i = 0; i < list1.Count(); i++){
list3[i] = list1[i] && list2[i]
}

If you must to this with linq, you could import the MoreLinq library and then combine .interleave with .batch

https://morelinq.github.io/2.0/ref/api/html/M_MoreLinq_MoreEnumerable_Interleave__1.htm http://www.toplinestrategies.com/blogs/application-development/grouping-data-morelinq-batch-extension-method https://github.com/morelinq

0
Allumearz On

Here is a way using only 1 loop to carry the index of the element in question...

static void Main(string[] args)
{
    var possibilities = new[] {
        new[] {1, 1, 0, 1, 1, 0, 0, 0},
        new[] {1, 1, 0, 1, 1, 0, 0, 1},
        new[] {0, 1, 1, 0, 1, 1, 0, 1}
    };

    IList<int> output = new List<int>();

    for (int i = 0; i < possibilities[0].Length; i++)
    {
         output.Add(possibilities.All(x => x.ElementAt(i) == possibilities[0][i]) ? 1 : 0);
    }

    Console.WriteLine("[{0}]", string.Join(", ", output));

    Console.ReadKey();
}

Returns [0, 1, 1, 1, 1, 1, 1, 0] for first example. Returns [0, 1, 0, 0, 1, 0, 1, 0] for second example.