Reputation: 145
Let's say I have a list of ints in c# like this:
List<int> numbers = new List<int>() { 1, 2, 7, 20, 3 };
Is there a way of checking that it has alternating odd and even numbers in it (like in the example above: if one if them is even then the next must be odd or vice versa)?
I know it's simple to check it in a loop, but I'm trying to implement this using LINQ and extension methods only.
Upvotes: 2
Views: 524
Reputation: 186813
Let's analyze the problem. What does it mean alternating parity?
index : value : value + index
------------------------------
0 : 1 : 1 - note, all sums are odd
1 : 2 : 3
2 : 7 : 9
....
Or
index : value : value + index
------------------------------
0 : 2 : 2 - note, all sums are even
1 : 1 : 2
2 : 6 : 8
....
As you can see (and you can easily prove it) alternating parity means that
index + value
sums are either all odd
or all even
. Let's check it with a help of Linq:
List<int> numbers = new List<int>() { 1, 2, 7, 20, 3, 79 };
bool result = numbers
.DefaultIfEmpty()
.Select((item, index) => Math.Abs((long)item + (long)index))
.Aggregate((s, a) => s % 2 == a % 2 ? s % 2 : -1) >= 0;
Notes to the implementation:
DefaultIfEmpty()
- empty sequence has all (all zero) values alternating; however, Aggregate
has nothing to aggregate and throws exception. Let's turn empty sequence into one element sequence.(long)
in order to prevent integer overflow (int.MaxValue + index
can well be out of int
range)Math.Abs
: c# can return negative remainder (e.g. -1 % 2
); we don't want an additional check for this, so let's work with absolute values-1 % 2 == -1
) in the final Aggregate
Extension Method solution I hope is easier to understand:
public static bool IsAlternating(this IEnumerable<int> source) {
if (null == source)
throw new ArgumentNullException(nameof(source));
bool expectedZero = false;
bool first = true;
foreach (int item in source) {
int actual = item % 2;
if (first) {
first = false;
expectedZero = actual == 0;
}
else if (actual == 0 && !expectedZero || actual != 0 && expectedZero)
return false;
expectedZero = !expectedZero;
}
return true;
}
Note, that the loop solution (extension method) is more efficient: it returns false
immediately when pattern doesn't meet.
Upvotes: 4
Reputation: 267
You can use Aggregate
to determine if a sequence is alternating.
Let's assume that if there's 0 or 1 elements then the result is true. You can modify this logic however you see fit.
Based on this an extension method can be created:
public static bool IsAlternatingParitySequenceVerbose(this IEnumerable<int> col)
{
// state:
// -1 - sequence is not alternating
// 0 - even
// 1 - odd
if (!col.Any())
return true;
//check if first value is even
var firstState = Math.Abs(col.First() % 2);
var IsAlternating = col.Skip(1).Aggregate(firstState, (state, val) =>
{
if (state == -1)
return -1;
var current = Math.Abs(val % 2);
if (current == state)
return -1;
return current;
});
return IsAlternating != -1;
}
And then make a one-liner out of it:
public static bool IsAlternatingParitySequence(this IEnumerable<int> col) =>
!col.Any()
|| col.Skip(1).Aggregate(Math.Abs(col.First() % 2), (state, val) =>
state == -1
? -1
: Math.Abs(val % 2) is var current && current == state
? -1
: current
) != -1;
Upvotes: 0
Reputation: 1
You can do with LINQ in this way. you can check if there is atelast one even item at add place or an odd item at even place.
List<int> numbers = new List<int>() { 1, 2, 7, 20, 3 };
var temp = numbers.Where((x, i) => (i % 2 == 0 && x % 2 == 0) || (i % 2 == 1 && x % 2 == 1)).Take(1);
int count = temp.Count();
if(count == 0)
{
//true
}
else
{
//false
}
Note: Assuming that you are expecting even numbers at even place and odd numbers at an odd place.
Upvotes: 0