I have array of arrays. Suppose I want to count how much elements out of all 9 is equal to "a".
string[][] arr = new string[3][] {
new string[]{"a","b","c"},
new string[]{"d","a","f"},
new string[]{"g","a","a"}
};
How can I do it using Enumerable extension methods (Count, Where, etc)?
You simply need a way to iterate over the subelements of the matrix, you can do this using SelectMany(), and then use Count():
int count = arr.SelectMany(x => x).Count(x => x == "a");
Producing:
csharp> arr.SelectMany(x => x).Count(x => x == "a");
4
Or you could Sum() up the counts of the Count()s of each individual row, like:
int count = arr.Sum(x => x.Count(y => y == "a"));
Producing again:
csharp> arr.Sum(x => x.Count(y => y == "a"));
4
You can flatten all arrays into single sequence of strings with SelectMany and then use Count extension which accepts predicate:
arr.SelectMany(a => a).Count(s => s == "a")
Related
I have a requirement to rank the array elements and the array has duplicate values. I tried following this Ranking items in a list with LINQ but this doesn't work when the array has duplicate values in it. Any easy way to do it in c#?
For Example :
input = [650,150,150,200]
output = [1,3,3,2]
For Example :
input = [650,200,200,150]
output = [1,2,2,3]
Update: The requirement is as below, what if I add one more element to the array
Ex: [650,150,150,200,100] output needs to be [1,3,3,2,5] instead of [1,3,3,2,4]
You can create a dictionary as rank-lookup source:
int[] array = new[] {650,150,150,200};
Dictionary<int, int> numRanks = array
.GroupBy(i => i)
.OrderByDescending(g => g.Key)
.Select((g, index) => (num:g.Key, rank:index+1))
.ToDictionary(x => x.num, x => x.rank);
int[] result = array.Select(i => numRanks[i]).ToArray();
For your updated requirement you could use a similar approach using a Lookup<TKey, TValue>:
var rankLookup = array
.OrderByDescending(i => i)
.Select((num, index) => (num, index))
.ToLookup(x => x.num, x => x.index + 1);
int[] result = array.Select(i => rankLookup[i].First()).ToArray();
The lookup is like a dictionary that allows duplicate keys. You need to use First here because you are just interested in the rank. If you'd use Count() you'd know how many duplicates it had.
You could create an array of items, distinct and in order, then use the indices to determine the rank of each item.
var ranks = input.Distinct().OrderByDescending(x => x).ToArray();
var ranked = input.Select(x => Array.IndexOf(ranks, x) + 1);
Working example
Update after comment
If rankings need to be skipped, just remove the Distinct:
var ranks = input.OrderByDescending(x => x).ToArray();
var ranked = input.Select(x => Array.IndexOf(ranks, x) + 1);
Array.IndexOf will take the first element when there are duplicates.
Working example
Was doing this problem https://leetcode.com/problems/how-many-numbers-are-smaller-than-the-current-number/submissions/
Input: nums = [8,1,2,2,3]
Output: [4,0,1,1,3]
Explanation: For
nums[0]=8 there exist four smaller numbers than it (1, 2, 2 and 3).
For nums[1]=1 does not exist any smaller number than it. For nums[2]=2
there exist one smaller number than it (1). For nums[3]=2 there exist
one smaller number than it (1). For nums[4]=3 there exist three
smaller numbers than it (1, 2 and 2).
as LINQ-y as possible and came up with a solution which is only half LINQ :(
public class Solution {
public int[] SmallerNumbersThanCurrent(int[] nums) {
var groups = nums
.Select((val, index) => new { index, val })
.GroupBy(x => x.val)
.OrderBy(g => g.Key)
.Select(g => g.Select(x => x.index).ToArray());
var arr = new int[nums.Length];
int numSmaller = 0;
foreach (var indices in groups)
{
foreach (var index in indices)
{
arr[index] = numSmaller;
}
numSmaller += indices.Length;
}
return arr;
}
}
Is anyone here clever enough to help me figure out a way to LINQ-ify the second half of the solution? Preferably O(n log n) as code I have.
I hope I understood your question. You could do the following.
public int[] SmallerNumbersThanCurrent(int[] nums)
{
return nums.Select(x=> nums.Count(c=> c<x)).ToArray();
}
Though I don't think using one LINQ is a good idea here, it is possible to get rid of the foreach you have like this, assuming approximate nlog(n) is required:
nums.Select((num, index) => new { num, index })
// order number
.OrderBy(x => x.num)
// select number with their original index in nums and
// their order in the ordered collection
.Select((x, order) => new { x.num, x.index, order })
// Group the result by number
.GroupBy(x => x.num)
// Consolidate order in the ordered collection by selecting the minimum
// possible order
.Select(g => new
{
numWithOrder = g.Select(_ => new
{
num = _,
minOrder = g.First().order
})
})
// Flatten the collection
.SelectMany(g => g.numWithOrder)
// There should be minOrder number of results in the original collection
// are smaller than the number
.Select(x => new { x.num.index, result = x.minOrder })
// Restore as per original index
.OrderBy(x => x.index)
// Select final result
.Select(x => x.result)
As you might have seen, LINQ kills the readability of the code.
Here is another solution. It uses the Scan extension method from the System.Interactive package, for counting by accumulation the numbers that are smaller than the numbers of the current group.
public int[] SmallerNumbersThanCurrent(int[] nums)
{
return nums
.Select((x, i) => (Item: x, Index: i))
.GroupBy(x => x.Item, x => x.Index)
.OrderBy(g => g.Key)
.Scan(seed: (Indices: Enumerable.Empty<int>(), Counter: 0),
accumulator: (acc, x) => (x, acc.Counter + acc.Indices.Count()))
.SelectMany(acc => acc.Indices,
(acc, element) => (Index: element, CountOfSmallerNumbers: acc.Counter))
.OrderBy(x => x.Index)
.Select(x => x.CountOfSmallerNumbers)
.ToArray();
}
This solution is arguably even more obscure and unreadable than weichch's solution. 😃
The signature of the Scan extension method:
public static IEnumerable<TAccumulate> Scan<TSource, TAccumulate>(
this IEnumerable<TSource> source, TAccumulate seed,
Func<TAccumulate, TSource, TAccumulate> accumulator);
Generates a sequence of accumulated values by scanning the source sequence and applying an accumulator function.
public int[] SmallerNumbersThanCurrentShorter(int[] nums)
{
return (from x in nums select (from y in nums where y < x select y).Count()).ToArray();
}
just do this!!
I'm new to c# so go easy on me. Anyways, I made a list of numbers
List<int> numbers = new List<int>();
and I want to make a list of each number and its count/frequency.
var grouped = numbers
.GroupBy(i => i)
.Select(i => new { Number = i.Key, Count = i.Count() });
In locals, I can see the group, which has an IEnumerator interface with all of the numbers and their count values image of what I'm talking about. So is there a way to make a list with the numbers and their frequency/count?
Thank you.
IEnumerable<T> is a sequence so it doesn't own a count. But Enumerable.Count is an extension method of IEnumerable<T>
That is, you don't necessarily need to convert an IEnumerable<T> into a List<T>:
var grouped = numbers
.GroupBy(i => i)
.Select(i => new { Number = i.Key, Count = i.Count() });
var groupedCount = grouped.Count();
// You may iterate grouped
foreach(var value in grouped)
{
Console.WriteLine($"{value.Number} {value.Count}");
}
If you really need List<T> semantics, you just need to call Enumerable.ToList:
var grouped = numbers
.GroupBy(i => i)
.Select(i => new { Number = i.Key, Count = i.Count() })
.ToList();
In the other hand, you may directly convert everything into a string as follows:
var groupText = string.Join("\n", numbers
.GroupBy(i => i)
.Select(i => $"Number: {i.Key} Count: {i.Count()}"))
To get a list, you just need to call ToList(), for example:
var grouped = numbers
.GroupBy(i => i)
.Select(i => new { Number = i.Key, Count = i.Count() })
.ToList();
However, you really don't need to do that, you can simply loop over the enumerable as it stands:
foreach(var item in grouped)
{
Console.WriteLine($"{item.Number} occurs {item.Count} times");
}
Sounds like you want ToDictionary with the number as key and the frequency as value:
var grouped = numbers
.GroupBy(i => i)
.Select(i => new { Number = i.Key, Count = i.Count() })
.ToDictionary(x => x.Number, x => x.Count);
Now you can easily print every number and its frequency by looping the dictionary.
In fact you don´t even need neither ToDictionary nor your Select, as the IGrouping returned from GroupBy also derives from IEnumerable which is why you can iterate over it.
foreach(var g in grouped = numbers.GroupBy(i => i))
{
var number = g.Key;
var freq = g.Count();
}
Let say you have list of items and you want to partition them, make operation on one partition and concatenate partitions back into list.
For example there is list of numbers and I want to partition them by parity, then reverse odds and concatenate with evens. [1,2,3,4,5,6,7,8] -> [7,5,3,1,2,4,6,8]
Sounds simple, but I've got stuck on merging back two groups. How would you do it with LINQ?
IEnumerable<int> result = Enumerable.Range(0, 1000)
.GroupBy(i => i % 2)
.Select(p => p.Key == 1 ? p.Reverse() : p)
.??? // need to concatenate
Edit
[1,2,3] is the representation of array which I want to get as the result, not output, sorry if I confused you by that.
The GroupBy method returns an IEnumerable<IGrouping<TKey, TSource>>. As IGrouping implements IEnumerable, you can use SelectMany to concatenate multiple IEnumerable<T> instances into one.
Enumerable.Range(0, 1000)
.GroupBy(i => i % 2)
.Select(p => p.Key == 1 ? p.Reverse() : p)
.OrderByDescending(p => p.Key)
.SelectMany(p => p);
There are a few ways to achieve this,
so if we start with your function
Enumerable.Range(0, 1000)
.GroupBy(i => i % 2)
.Select(p => p.Key == 1 ? p.Reverse() : p)
you could then use an Aggregate
.Aggregate((aggrgate,enumerable)=>aggrgate.Concat(enumerable))
this will then go though your list of results and concat them all into a collection and return it, you just need to make sure that aggrgate and enumerable are the same type in this case a IEnumerable<int>
another would be to call SelectMany()
.SelectMany(enumerable=>enumerable)
this then likewise pulls all the enumerables together into a single enumerable, again you need to ensure the types are IEnumerable<int>
other options would be to hard code the keys as Tim is suggesting or pull out of linq and use a loop
You could use this approach using a Lookup<TKey, TElement>:
var evenOddLookup = numbers.ToLookup(i => i % 2);
string result = String.Join(",", evenOddLookup[1].Reverse().Concat(evenOddLookup[0]));
If you don't want a string but an int[] as result:
int[] result = evenOddLookup[1].Reverse().Concat(evenOddLookup[0]).ToArray();
You could do something like this.
var number = string.Join(",",
Enumerable.Range(0, 1000)
.GroupBy(i => i % 2) // Separate even/odd numbers
.OrderByDescending(x=>x.Key) // Sort to bring odd numbers first.
.SelectMany(x=> x.Key ==1? // Sort elements based on even or odd.
x.OrderByDescending(s=>s)
: x.Where(s=> s!=0).OrderBy(s=>s))
.ToArray());
string output = string.Format("[{0}]", number);
Check this Demo
Just use OrderBy like this:
List<int> arr = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8 };
var result = arr.OrderBy(i => i % 2 == 0 ? 1 : 0)
.ThenBy(i => i % 2 == 0 ? i : int.MaxValue)
.ThenByDescending(i => i);
This should give you your desired result as you want:
[1,2,3,4,5,6,7,8] will be converted into [7,5,3,1,2,4,6,8]
Lets say i have an array
byte[] myarr = {1,4,3,4,1,2,1,2,4,3,1,4,2};
myarr will be of length 13 (0-12 Index) which will also be the length of int[] val.
int[] val = new int[13];
I want to check index of myarr where its value is 4 i.e. 1,3,8,11.
Then i want
val[1]++;
val[3]++;
val[8]++;
val[11]++;
One way of doing this is using for loop
for(int i=0; i<myarr.length; i++)
{
if(myarr[i] == 4)
val[i]++;
}
We can use Array.indexof but it returns the first index of that value meaning that value has to be unique and my myarr has lots of same values.
Can this be done using linq?
This is what I ended up doing in LINQ (update included):
myarr.Select((b, i) => b == 4 ? i : -1)
.Where(i => i != -1)
.ToList().ForEach(i => val[i]++);
Your non-LINQ version is obviously much more succinct and readable, so I think you should use that.
You can, but it won't be simpler. LINQ will only help you with the query part, the update part has to be done in a foo loop, but since the array contains value types you need to get indexes from your LINQ-query and not the actual values and you have won nothing.
You can use an anonymous type to store the index:
int[] newIndices = myarr.Select((i, index) => new { Index = index, Value = i })
.Where (x => x.Value == 4)
.Select(x => x.Index + 1)
.ToArray();
Edit: Finally i've understood your question ;)
myarr.Select((i, index) => new { Index = index, Value = i })
.Where(x => x.Value == 4)
.Select(x => x.Index)
.ForEach(i => myarr[i]++);