select from grouped collection - c#

I have a table (HallPlan) with fields:
Row, Seat, Width, Height, X, Y
And grouped collection by Y:
var ordered = hallPlans.GroupBy(s => s.Y).OrderBy(o => o.Key);
now I want to select max X from all minimum X for each group. For example ordered contains three groups, we have:
first group contains 3 records with X coordinates: 15, 50, 80;
second group contains: 16, 50,80;
and last group contains: 15, 40, 60;
select all min from each groupes: 15, 16, 15
and select max from the above: 16.
How can I do this with LINQ?
Thanks.

You can use the Min and Max functions.
class HallPlan { public int X { get; set; } }
...
var groups = new[]{
new []{new HallPlan{X=15},new HallPlan{X=50},new HallPlan{X=80}},
new []{new HallPlan{X=16},new HallPlan{X=50},new HallPlan{X=80}},
new []{new HallPlan{X=15},new HallPlan{X=40},new HallPlan{X=60}}
};
var maxmin = groups.Select(g => g.Min(h => h.X)).Max(); // result is 16
groups.Select(g => g.Min(h => h.X)) will select the minimun HallPlan.X from each group, and .Max() will then return the biggest value of those.

var allMinX = ordered.Select(g => g.Min(s => s.X));
var minMax = allMinX.Max();

Related

LINQ - Join both lists by last digit

A sequence of positive integers integerList1 and integerList2 are given. All values in each sequence are different.
Get a set (list of NumberPair values) of all value pairs that satisfy the following conditions:
the first element of the pair belongs to the sequence integerList1,
the second belongs to
integerList2
both elements end with the same digit.
The NumberPair type includes
Value 1, Value 2 fields.
The resulting NumberPair list must be sorted in ascending order
by the first field, and if they are equal, by the second.
Here is an example:
integerList1: new[] { 1, 12, 4, 5, 78 }
integerList2: new[] { 1, 42, 75, 65, 8, 97 }
Expected result:
expected: new[]
{
new NumberPair{Item1 = 1, Item2 = 1},
new NumberPair{Item1 = 5, Item2 = 65},
new NumberPair{Item1 = 5, Item2 = 75},
new NumberPair{Item1 = 12, Item2 = 42},
new NumberPair{Item1 = 78, Item2 = 8}
}
I tried to solve like this
var lastDigitsGroups1 = integerList1.GroupBy(num => num % 10).ToDictionary(kvp => kvp.Key, kvp => kvp.ToList());
var lastDigitsGroups2 = integerList2.GroupBy(num => num % 10).ToDictionary(kvp => kvp.Key, kvp => kvp.ToList());
var intersection = lastDigitsGroups1.Keys.Intersect(lastDigitsGroups2.Keys);
foreach (var item in intersection)
{
var np = new NumberPair { Item1 = lastDigitsGroups1[item].FirstOrDefault(), Item2 = lastDigitsGroups2[item].FirstOrDefault() };
yield return np;
}
However, it should be done only using LINQ and even with one LINQ query.
Join both lists by keys as below:
var result = (from a in integerList1
join b in integerList2 on (a % 10) equals (b % 10)
select new NumberPair { Item1 = a, Item2 = b }
)
.OrderBy(x => x.Item1)
.ThenBy(x => x.Item2)
.ToList();
Or
var result = integerList1.Join(integerList2,
x => x % 10,
y => y % 10,
(x, y) => new { x, y })
.Select(x => new NumberPair { Item1 = x.x, Item2 = x.y })
.OrderBy(x => x.Item1)
.ThenBy(x => x.Item2)
.ToList();
Demo # .NET Fiddle
I honestly don't understand your approach, it does not seem to do what you have mentioned in your conditions. If i just take them as requirement, use Enumerable.Zip:
var result = integerList1.Zip(integerList2, (i1, i2) => new NumberPair{Item1 = i1, Item2 = i2} )
.OrderBy(np => np.Item1)
.ThenBy(np => np.Item2);

List of most occurrences of element

I have a list let's say {1,1,2,2,3,3,3,4,4,4}. I want to find a List of the elements that occur the most often (it has to be a list as there can be a situation like here that 3 and 4 occur most and I need to get that information. How can I achieve this using LINQ?
var highCountItems = source
.GroupBy(item => item)
.GroupBy(g => g.Count(), g => g.Key)
.OrderByDescending(counts => counts.Key)
.First();
int theCount = highCountItems.Key;
var theItems = highCountItems.ToList();
By Grouping:
var grp = list.GroupBy(i => i).ToList();
int max = grp.Max(c => c.Count());
var most = grp.Where(d => d.Count() == max)
.Select(c => c.Key).ToList();
First you will have to group the numbers followed by ordering them so you will get the most frequently occurring number on the top.
int[] numbers = { 1, 1, 2, 2, 3, 3, 3, 4, 4, 4 };
var groups = numbers.GroupBy(i => i).OrderByDescending(g => g.Count());
foreach (var group in groups)
{
// group.Key -> Represents the number in the list
}
The groups variable will contain all the groups formed from the numbers list ordered by their occurrence, meaning the first group will be the top most occurring group followed by the next. In case of same occurrences, the groups will be ordered by their occurrence in the list for example 3 & 4 have equal occurrences whereas 3 comes first before 4 and hence the group formation will be in same order.
A little complicated
var lst_input = new List<int>(new int[] { 1, 1, 2, 2, 3, 3, 3, 4, 4, 4 });
var result = lst_input.Where(x => lst_input.Max(y => lst_input.Count(z => z == y)) == lst_input.Count(z => z == x)).Distinct().ToList();
But the above code is not effective when dealing with a really big array, since finding max is re-run for each element, and we could distinct the list in the first place. Another more efficient way:
var lst_input = new List<int>(new int[] { 1, 1, 2, 2, 3, 3, 3, 4, 4, 4 });
var tmp = lst_input.Distinct();
var max_val = tmp.Max(y => lst_input.Count(z => z == y));
var result = tmp.Where(x => max_val == lst_input.Count(z => z == x)).ToList();

How to rank a list with original order in c#

I want to make a ranking from a list and output it on original order.
This is my code so far:
var data = new[] { 7.806468478, 7.806468478, 7.806468478, 7.173501754, 7.173501754, 7.173501754, 3.40877696, 3.40877696, 3.40877696,
4.097010736, 4.097010736, 4.097010736, 4.036494085, 4.036494085, 4.036494085, 38.94333318, 38.94333318, 38.94333318, 14.43588131, 14.43588131, 14.43588131 };
var rankings = data.OrderByDescending(x => x)
.GroupBy(x => x)
.SelectMany((g, i) =>
g.Select(e => new { Col1 = e, Rank = i + 1 }))
.ToList();
However, the result will be order it from descending:
What I want is to display by its original order.
e.g.: Rank = 3, Rank = 3, Rank = 3, Rank = 4, Rank = 4, Rank = 4, etc...
Thank You.
Using what you have, one method would be to keep track of the original order and sort a second time (ugly and potentially slow):
var rankings = data.Select((x, i) => new {Item = x, Index = i})
.OrderByDescending(x => x.Item)
.GroupBy(x => x.Item)
.SelectMany((g, i) =>
g.Select(e => new {
Index = e.Index,
Item = new { Col1 = e.Item, Rank = i + 1 }
}))
.OrderBy(x => x.Index)
.Select(x => x.Item)
.ToList();
I would instead suggest creating a dictionary with your rankings and joining this back with your list:
var rankings = data.Distinct()
.OrderByDescending(x => x)
.Select((g, i) => new { Key = g, Rank = i + 1 })
.ToDictionary(x => x.Key, x => x.Rank);
var output = data.Select(x => new { Col1 = x, Rank = rankings[x] })
.ToList();
As #AntonínLejsek kindly pointed out, replacing the above GroupBy call with Distinct() is the way to go.
Note doubles are not a precise type and thus are really not a good candidate for values in a lookup table, nor would I recommend using GroupBy/Distinct with a floating-point value as a key. Be mindful of your precision and consider using an appropriate string conversion. In light of this, you may want to define an epsilon value and forgo LINQ's GroupBy entirely, opting instead to encapsulate each data point into a (non-anonymous) reference type, then loop through a sorted list and assign ranks. For example (disclaimer: untested):
class DataPoint
{
decimal Value { get; set; }
int Rank { get; set; }
}
var dataPointsPreservingOrder = data.Select(x => new DataPoint {Value = x}).ToList();
var sortedDescending = dataPointsPreservingOrder.OrderByDescending(x => x.Value).ToList();
var epsilon = 1E-15; //use a value that makes sense here
int rank = 0;
double? currentValue = null;
foreach(var x in sortedDescending)
{
if(currentValue == null || Math.Abs(x.Value - currentValue.Value) > epsilon)
{
currentValue = x.Value;
++rank;
}
x.Rank = rank;
}
From review of the data you will need to iterate twice over the result set.
The first iteration will be to capture the rankings as.
var sorted = data
.OrderByDescending(x => x)
.GroupBy(x => x)
.Select((g, i) => new { Col1 = g.First(), Rank = i + 1 })
.ToList();
Now we have a ranking of highest to lowest with the correct rank value. Next we iterate the data again to find where the value exists in the overall ranks as:
var rankings = (from i in data
let rank = sorted.First(x => x.Col1 == i)
select new
{
Col1 = i,
Rank = rank.Rank
}).ToList();
This results in a ranked list in the original order of the data.
A bit shorter:
var L = data.Distinct().ToList(); // because SortedSet<T> doesn't have BinarySearch :[
L.Sort();
var rankings = Array.ConvertAll(data,
x => new { Col1 = x, Rank = L.Count - L.BinarySearch(x) });

get nearest tens-place number above and below any number

I want to be able to get the nearest x-tens-place number above and below any number using C#
For example, if I have a 4-digit number and I want all the closes number above and below ending and set of 2-digit number like 30, 50, 80, or 00 then
2126 => 2100 and 2130
2146 => 2130 and 2150
2183 => 2180 and 2200
I want to be able to do this below 1 too, like if my set of levels are 0.0030, 0.0050, 0.0080 and 0.0000 then if I had the following numbers
1.0026 => 1.0000 and 1.0030
1.0046 => 1.0030 and 1.0050
1.0083 => 1.0080 and 1.0100
The purpose of this is to calculate hi/lo ranges around a given asset price and a set of range values.
Because this isn't really anything to do with 10's rounding you have to specify the number of digits that you want to truncate with and then iterate over the "set point" values to find the two closest points to the given input.
This is what I came up with:
Func<double, double[], int, double> lower = (x, sps, d) =>
sps
.Select(sp => sp + Math.Truncate(Math.Pow(10.0, d) * x) / Math.Pow(10.0, d))
.Where(v => v <= x)
.Last();
Func<double, double[], int, double> upper = (x, sps, d) =>
sps
.Select(sp => sp + Math.Truncate(Math.Pow(10.0, d) * x) / Math.Pow(10.0, d))
.Where(v => v >= x)
.First();
My input data is:
var data = new []
{
new
{
setpoints = new double[] { 0, 30, 50, 80, 100 },
digits = -2,
values = new double[] { 2126, 2146, 2183 },
},
new
{
setpoints = new [] { 0.0, 0.003, 0.005, 0.008, 0.01 },
digits = 2,
values = new [] { 1.0026, 1.0046, 1.0083 },
},
};
The results were calculated as:
var results =
data
.SelectMany(
x => x.values,
(x, v) => new
{
value = v,
lower = lower(v, x.setpoints, x.digits),
upper = upper(v, x.setpoints, x.digits)
});
The results I got were as expected:

how would i use linq to find the most occured data in a data set?

List<int> a = 11,2,3,11,3,22,9,2
//output
11
This may not be the most efficient way, but it will get the job done.
public static int MostFrequent(IEnumerable<int> enumerable)
{
var query = from it in enumerable
group it by it into g
select new {Key = g.Key, Count = g.Count()} ;
return query.OrderByDescending(x => x.Count).First().Key;
}
And the fun single line version ...
public static int MostFrequent(IEnumerable<int> enumerable)
{
return (from it in enumerable
group it by it into g
select new {Key = g.Key, Count = g.Count()}).OrderByDescending(x => x.Count).First().Key;
}
a.GroupBy(item => item).
Select(group => new { Key = group.Key, Count = group.Count() }).
OrderByDescending(pair => pair.Count).
First().
Key;
Another example :
IEnumerable<int> numbers = new[] { 11, 2, 3, 11, 3, 22, 9, 2 };
int most = numbers
.Select(x => new { Number = x, Count = numbers.Count(y => y == x) })
.OrderByDescending(z => z.Count)
.First().Number;

Categories

Resources