Single enumeration with multiple filters - c#

Given the following contrived example:
public bool NumberOfEvensEqualsNumberOfOdds(IEnumerable<int> numbers) {
var numberOfEvens = numbers.Count(x => x % 2 == 0);
var numberOfOdds = numbers.Count(x => x % 2 != 0);
return numberOfEvens == numberOfOdds;
}
This works but requires multiple enumerations of the collection.
Is it possible to re-write this to use a single linq expression that enumerates the collection once.
NOTE: I'm trying to solve the general case of comparing counts with two filters so try and ignore the fact that the sample is about odd and even numbers.
I have included a sample .NET Fiddle

A bit cryptic at first glance, but only iterates through the collection once.
Func<int, bool> isEven = n => n % 2 == 0;
Func<int, bool> isFive = n => n == 5;
int diff = numbers.Aggregate(0, (sum, next) => isEven(next) ? sum + 1 : isFive(next) ? sum - 1 : sum);
For each item in the collection, it checks the two conditions. If the first condition applies, it adds one to the aggregate variable; if the second applies, it subtracts one. The end result is the difference between the number of items that meet the first criteria and the number of items that meet the second.

You can use GroupBy:
var groupedByCondition =
= numbers.GroupBy(x => x % 2 == 0)
.Select(x => new { Result = x.Key, Count = g.Count() })
.ToArray();
return groupedByCondition.Length == 2
&& groupedByCondition[0].Count == groupedBycondition[1].Count;

If you have two disjoint filters (that is, an item can't simultaneously satisfy both filters), then BJ Myer's answer may be as simple and efficient as you can get.
If you have two not-necessarily disjoint filters, then you can use the following slight variation which always evaluates both filters for every item:
public static bool NumberOfEvensEqualsNumberOfOdds(IEnumerable<int> numbers)
{
// Compute
// numbers.Count(x => x % 2 == 0) - numbers.Count(x => x % 2 != 0)
// or equivalently,
// numbers.Sum(x => x % 2 == 0 ? 1 : 0) - numbers.Sum(x => x % 2 != 0 ? 1 : 0)
int sum = numbers.Sum(x =>
(x % 2 == 0 ? 1 : 0) -
(x % 2 != 0 ? 1 : 0));
return sum == 0;
}
If you have an arbitrary number of not-necessarily disjoint filters, then you can use the following generic method:
public static bool HasEqualSizeSubsets<T>(
IEnumerable<T> items, params Func<T, bool>[] filters)
{
var indexedFilters = filters
.Select((filter, index) => new { Filter = filter, Index = index })
.ToArray(); // to avoid repeated object allocations later
IEnumerable<int> subsetSizes = items
.SelectMany(item => indexedFilters
.Where(indexedFilter => indexedFilter.Filter(item))
.Select(indexedFilter => indexedFilter.Index))
.GroupBy(index => index)
.Select(grouping => grouping.Count());
return subsetSizes.Distinct().Count() == 1;
}
HasEqualSizeSubsets looks complicated, but the basic idea is straightforward:
First, we get the array index of each filter passed in the filters array parameter.
Then, for each item in items, we get the index of each filter that the item satisfies. (For example, if the item satisfies just the first filter, we output a "0". If the item satisfies the first two filters, we output "0" and "1".) The result of the SelectMany call is a sequence of filter indexes.
Next, we count the number of times each index appears. The result of the GroupBy and Select calls is a sequence of subset sizes.
Finally, we check whether all the subset sizes are the same unique value.
HasEqualSizeSubsets could be used like this:
public static bool NumberOfEvensEqualsNumberOfOdds(IEnumerable<int> numbers)
{
return HasEqualSizeSubsets(numbers, x => x % 2 == 0, x => x % 2 != 0);
}

You code do it this way using .Aggregate:
public bool NumberOfEvensEqualsNumberOfOdds(IEnumerable<int> numbers)
{
var result =
numbers
.Aggregate(
new { evens = 0, odds = 0 },
(a, x) =>
{
a = x % 2 == 0
? new { evens = a.evens + 1, a.odds }
: a;
a = x % 2 != 0
? new { a.evens, odds = a.odds + 1 }
: a;
return a;
});
return result.evens == result.odds;
}
The logic could be updated to compute and number of different projections from the source numbers.

If you want to you check two different conditions and loop it only one time.
you can use foreach loop and have your own logic inside
public static bool NumberOfEvensEqualsNumberOfOdds(IEnumerable<int> numbers)
{
int evenCount = 0;
int oddCount = 0;
foreach (var item in numbers)
{
if (item % 2 == 0)
evenCount++;
else if (item % 2 != 0)
oddCount++;
}
return evenCount == oddCount;
}

Related

Get Items that sum Closest to Given Value

I have a list of values. lets say they're {1,2,3,4,5}
and I want to find the combination that sums closest to a given value
so for example if i entered 8 then the function could return
either {3,5} or {1,3,4}. either of those would be exact and i would just take the one with the least indexes 3+5
if there isn't a value that is exact for example 9.45 it would return the value
closest without going over the threshold {4,5}
I'm not sure where i would even start with this. i think it may be possible with a linq query...
static IEnumerable<int> GetClosestValues(IReadOnlyList<int> c, int t)
{
var s=0m;
return c.Select((x, i) => i).OrderByDescending(i => c[i]).Where(i => (s += c[i]) <= t).OrderBy(i => i);
}
Seems to work... not optimized though
The key is just to find all permutations of the set, and then filter (less or equal than threshold value) and order (by distance to threshold value, then by size of the set) them via LINQ:
var source = Enumerable.Range(1, 5).Select(x => (double)x).ToArray();
var permutations = Enumerable.Range(1, source.Length)
.SelectMany(x => Utils.GetOrderedPermutations(source, x))
.Dump();
var threshold = 9.45;
var result = permutations
.Select(x => x.ToArray())
.Select(x => new { Set = x, Sum = x.Sum() })
.Where(x => x.Sum <= threshold)
.OrderBy(x => threshold - x.Sum)
.ThenBy(x => x.Set.Length)
.FirstOrDefault()
.Dump();
GetOrderedPermutations is taken from this answer:
public class Utils
{
public static IEnumerable<T> Yield<T>(T value)
{
yield return value;
}
public static IEnumerable<IEnumerable<T>> GetOrderedPermutations<T>(IEnumerable<T> source, int k)
{
if (k == 0) return new[] { Enumerable.Empty<T>() };
int length = source.Count();
if (k == length) return new[] { source };
if (k > length) return Enumerable.Empty<IEnumerable<T>>();
return GetOrderedHelper<T>(source, k, length);
}
private static IEnumerable<IEnumerable<T>> GetOrderedHelper<T>(IEnumerable<T> source, int k, int length)
{
if (k == 0)
{
yield return Enumerable.Empty<T>();
yield break;
}
int i = 0;
foreach (var item in source)
{
if (i + k > length) yield break;
var permutations = GetOrderedHelper<T>(source.Skip(i + 1), k - 1, length - i);
i++;
foreach (var subPerm in permutations)
{
yield return Yield(item).Concat(subPerm);
}
}
}
}

Check property of List if it is of a given type

I have this class structure
public class A
{
int number;
}
public class B : A
{
int otherNumber;
}
I want to search a list of A for items, where the number is greater than a given value and where the otherNumber is greater than another given value, if they are of type B. I am looking for something like:
var results = list.Where(x => x.number>5 && x.otherNumber>7).ToList();
Where list is a List<A>.
My current approach is:
var results = list.Where(x => x.number>5);
foreach(var result in results)
{
B b = result As B;
if(b!=null && b.otherNumber>7)
[...]
}
You can filter by number field (assume fields are public). And then filter by otherNumber field if a is of B type. Otherwise second filtering will just skip
list.Where(a => a.number > 5).Where(a => !(a is B) || ((B)a).otherNumber > 7)
Maybe more readable way:
list.Where(a => {
var b = a as B;
return a.number > 5 && (b == null || b.otherNumber > 7);
})
Or query syntax
from a in list
let b = a as B
where a.number > 5 && (b == null || b.otherNumber > 7)
Select all Bs that aren't bigger than 7:
var badBs = list.OfType<B>().Where(x => x.otherNumber <= 7);
Select all items that match the first requirement, except those that don't match the second requirement:
var results = list.Where(x => x.number > 5).Except(badBs);
Here - only B objects will be match condition, so most short form is:
list.OfType<B>().Where(x=>x.num1 > 5 && x.num2 < 7);

Find indices of particular items in the list using linq

I have a list of integers from 1 to 20. I want the indices of items which are greater than 10 using linq. Is it possible to do with linq?
Thanks in advance
Use the overload of Select which includes the index:
var highIndexes = list.Select((value, index) => new { value, index })
.Where(z => z.value > 10)
.Select(z => z.index);
The steps in turn:
Project the sequence of values into a sequence of value/index pairs
Filter to only include pairs where the value is greater than 10
Project the result to a sequence of indexes
public static List<int> FindIndexAll(this List<int> src, Predicate<int> value)
{
List<int> res = new List<int>();
var idx = src.FindIndex(x=>x>10);
if (idx!=-1) {
res.Add(idx);
while (true)
{
idx = src.FindIndex(idx+1, x => x > 10);
if (idx == -1)
break;
res.Add(idx);
}
}
return res;
}
Usage
List<int> test= new List<int>() {1,10,5,2334,34,45,4,4,11};
var t = test.FindIndexAll(x => x > 10);

LINQ method to group collection into subgroups with specified number of elements

Does there exist a LINQ method to group a given collection into subgroups with specified number of elements I mean, something like Scala's grouped method.
e.g. in Scala, List(89, 67, 34, 11, 34).grouped(2) gives List(List(89, 67), List(34, 11), List(34)).
In case such a method doesn't exist, what would be the LINQ way to do it?
Yes, you can. But you can argue if it's very pretty...
Int64[] aValues = new Int64[] { 1, 2, 3, 4, 5, 6 };
var result = aValues
.Select( ( x, y ) => new KeyValuePair<Int64, Int32>( x, y ) )
.GroupBy( x => x.Value / 2 )
.Select( x => x.Select( y => y.Key ).ToList() ).ToList();
How it works:
Select x and y from the original collection, where x is the actual value and y is the index of it in the given collection. Then group by integer devision of the index and the desired grouping length ( in this example 2 ).
Grouping by integer devision will round up to the lower - so 0 / 2 = 0, 1 / 2 = 0, etc. which will give us the needed grouping category value. This is what we are grouping against here.
For result select only the values grouped in lists and return them as a collection of lists.
Here is a website that seems to have some sample code to do what you want:
http://www.chinhdo.com/20080515/chunking/
So what you could do is take this method and create an extension method.
Extension method sample:
static class ListExtension
{
public static List<List<T>> BreakIntoChunks<T>(this List<T> list, int chunkSize)
{
if (chunkSize <= 0)
{
throw new ArgumentException("chunkSize must be greater than 0.");
}
List<List<T>> retVal = new List<List<T>>();
while (list.Count > 0)
{
int count = list.Count > chunkSize ? chunkSize : list.Count;
retVal.Add(list.GetRange(0, count));
list.RemoveRange(0, count);
}
return retVal;
}
}
You could try the approach shown in this answer to this similar question.
public static class GroupingExtension
{
public static IEnumerable<IEnumerable<T>> Grouped<T>(
this IEnumerable<T> input,
int groupCount)
{
if (input == null) throw new ArgumentException("input");
if (groupCount < 1) throw new ArgumentException("groupCount");
IEnumerator<T> e = input.GetEnumerator();
while (true)
{
List<T> l = new List<T>();
for (int n = 0; n < groupCount; ++n)
{
if (!e.MoveNext())
{
if (n != 0)
{
yield return l;
}
yield break;
}
l.Add(e.Current);
}
yield return l;
}
}
}
Use like this:
List<int> l = new List<int>{89, 67, 34, 11, 34};
foreach (IEnumerable<int> group in l.Grouped(2)) {
string s = string.Join(", ", group.Select(x => x.ToString()).ToArray());
Console.WriteLine(s);
}
Result:
89, 67
34, 11
34

Find the most occurring number in a List<int>

Is there a quick and nice way using linq?
How about:
var most = list.GroupBy(i=>i).OrderByDescending(grp=>grp.Count())
.Select(grp=>grp.Key).First();
or in query syntax:
var most = (from i in list
group i by i into grp
orderby grp.Count() descending
select grp.Key).First();
Of course, if you will use this repeatedly, you could add an extension method:
public static T MostCommon<T>(this IEnumerable<T> list)
{
return ... // previous code
}
Then you can use:
var most = list.MostCommon();
Not sure about the lambda expressions, but I would
Sort the list [O(n log n)]
Scan the list [O(n)] finding the longest run-length.
Scan it again [O(n)] reporting each number having that run-length.
This is because there could be more than one most-occurring number.
Taken from my answer here:
public static IEnumerable<T> Mode<T>(this IEnumerable<T> input)
{
var dict = input.ToLookup(x => x);
if (dict.Count == 0)
return Enumerable.Empty<T>();
var maxCount = dict.Max(x => x.Count());
return dict.Where(x => x.Count() == maxCount).Select(x => x.Key);
}
var modes = { }.Mode().ToArray(); //returns { }
var modes = { 1, 2, 3 }.Mode().ToArray(); //returns { 1, 2, 3 }
var modes = { 1, 1, 2, 3 }.Mode().ToArray(); //returns { 1 }
var modes = { 1, 2, 3, 1, 2 }.Mode().ToArray(); //returns { 1, 2 }
I went for a performance test between the above approach and David B's TakeWhile.
source = { }, iterations = 1000000
mine - 300 ms, David's - 930 ms
source = { 1 }, iterations = 1000000
mine - 1070 ms, David's - 1560 ms
source = 100+ ints with 2 duplicates, iterations = 10000
mine - 300 ms, David's - 500 ms
source = 10000 random ints with about 100+ duplicates, iterations = 1000
mine - 1280 ms, David's - 1400 ms
Here is another answer, which seems to be fast. I think Nawfal's answer is generally faster but this might shade it on long sequences.
public static IEnumerable<T> Mode<T>(
this IEnumerable<T> source,
IEqualityComparer<T> comparer = null)
{
var counts = source.GroupBy(t => t, comparer)
.Select(g => new { g.Key, Count = g.Count() })
.ToList();
if (counts.Count == 0)
{
return Enumerable.Empty<T>();
}
var maxes = new List<int>(5);
int maxCount = 1;
for (var i = 0; i < counts.Count; i++)
{
if (counts[i].Count < maxCount)
{
continue;
}
if (counts[i].Count > maxCount)
{
maxes.Clear();
maxCount = counts[i].Count;
}
maxes.Add(i);
}
return maxes.Select(i => counts[i].Key);
}
Someone asked for a solution where there's ties. Here's a stab at that:
int indicator = 0
var result =
list.GroupBy(i => i)
.Select(g => new {i = g.Key, count = g.Count()}
.OrderByDescending(x => x.count)
.TakeWhile(x =>
{
if (x.count == indicator || indicator == 0)
{
indicator = x.count;
return true;
}
return false;
})
.Select(x => x.i);
Here's a solution I've written for when there are multiple most common elements.
public static List<T> MostCommonP<T>(this IEnumerable<T> list)
{
return list.GroupBy(element => element)
.GroupBy(group => group.Count())
.MaxBy(groups => groups.Key)
.Select(group => group.Key)
.ToList();
}

Categories