How can i use distinct with count in Linq? - c#

The extention method below does not have Distinct and Count
public static IEnumerable<Something> ToFilterModel(this IEnumerable<Product> products)
{
var v = products
.SelectMany(x => x.ProductVariants)
.GroupBy(x => x.OptionId)
.Select(x => new
{
Id = x.Key.ToString(),
Items = x.Select(x => new Item { Id = x.ValueId, Text = x.Value.OptionValue })
});
return v;
}
Given the input below it should return 2 Items rows and not 3, since i am interested for ValueIds
and also Count by ValueIds
how should i modify it?
More spesifically it should return items with rows 1 and 2 and also
Count equal to 1 for the first row and Count equal to 2 for the second row.

You could group by ValueId the grouped options, like :
Items = x
.GroupBy(y => y.ValueId)
.Select(z => new Item { Id = z.Key, Text = z.First().Value.OptionValue, Count = z.Count() })
The result will be :
{
"Id":1,
"Items":[
{
"Id":1,
"Text":"text1",
"Count":1
},
{
"Id":2,
"Text":"text2",
"Count":2
}
]
}
NOTE : the Text is the count of grouped value ids.
The whole code :
var v = products
.SelectMany(x => x.ProductVariants)
.GroupBy(x => x.OptionId)
.Select(x => new
{
Id = x.Key.ToString(),
Items = x
.GroupBy(y => y.ValueId)
.Select(z => new Item { Id = z.Key, Text = z.First().Value.OptionValue, Count = z.Count() })
});

Related

How to Add Rownum to GroupBy Linq

I have a complex LINQ Query to extract Top students in my university. Here is the query :
var query = Db.Students.AsNoTracking().Where(...).AsQueryable();
var resultgroup = query.GroupBy(st => new
{
st.Student.CourseStudyId,
st.Student.EntranceTermId,
st.Student.StudyingModeId,
st.Student.StudyLevelId
}, (key, g) => new
{
CourseStudyId = key.CourseStudyId,
EntranceTermId = key.EntranceTermId,
StudyingModeId = key.StudyingModeId,
StudyLevelId = key.StudyLevelId,
list = g.OrderByDescending(x =>
x.StudentTermSummary.TotalAverageTillTerm).Take(topStudentNumber)
}).SelectMany(q => q.list).AsQueryable();
This Query give me top n students based on 4 parameters and on their TotalAverageTillTerm.
Now I want to add rownum for each group to simulate Total rank, for example Output is :
Now I want to Add TotalRank as rownumber like Sql. In the picture X1=1,X2=2,X3=3 and Y1=1,Y2=2,Y3=3
If I want to reduce problem. I only work on one group. Code Like this :
resultgroup = query.GroupBy(st => new
{
st.Student.StudyLevelId
}, st => st, (key, g) => new
{
StudyLevelId = key.StudyLevelId,
list = g.OrderByDescending(x =>
x.StudentTermSummary.TotalAverageTillTerm)
.Take(topStudentNumber)
}).SelectMany(q => q.list).AsQueryable();
list was a List of student but I see no sign of student having a rank property so I wrapped it into a annonimous type with rank.
var query = Db.Students.AsNoTracking().Where(...).AsEnumerable();
var resultgroup = query.GroupBy(st => new {
st.Student.CourseStudyId,
st.Student.EntranceTermId,
st.Student.StudyingModeId,
st.Student.StudyLevelId
})
.SelectMany( g =>
g.OrderByDescending(x =>x.StudentTermSummary.TotalAverageTillTerm)
.Take(topStudentNumber)
.Select((x,i) => new {
CourseStudyId = g.Key.CourseStudyId,
EntranceTermId = g.Key.EntranceTermId,
StudyingModeId = g.Key.StudyingModeId,
StudyLevelId = g.Key.StudyLevelId,
Rank = i+1
//studentPorperty = x.Prop1,
})
)
.AsQueryable();
Do you mean :
var query = Db.Students.AsNoTracking().Where(...).AsQueryable();
var resultgroup = query.GroupBy(st => new
{
st.Student.CourseStudyId,
st.Student.EntranceTermId,
st.Student.StudyingModeId,
st.Student.StudyLevelId
}, (key, g) => new
{
CourseStudyId = key.CourseStudyId,
EntranceTermId = key.EntranceTermId,
StudyingModeId = key.StudyingModeId,
StudyLevelId = key.StudyLevelId,
list = g.OrderByDescending(x =>
x.StudentTermSummary.TotalAverageTillTerm)
.Take(topStudentNumber)
.Select((x, i) => new { Item = x, TotalRank = i /* item number inside group */}),
StudentsInGroupCount = g.Count() // count group this items
}).SelectMany(q => q).AsQueryable();
To see the results :
foreach (var item in resultgroup.ToList())
{
item.list.ForEach(s => Console.WriteLine(s.TotalRank));
}

Summing up double lists in a Linq GroupBy

I need to group a large number of records which were recorded every minute into daily and bind them to a chart. These records have two fields the datetime value and the double list. I've tried something like this:
var result = Alldatas
.AsEnumerable()
.GroupBy(r => r.TimeStamp.Day)
.Select(x => new {
Day = x.Key,
Value = x.Sum(r => r.Value.Sum())
})
.OrderBy(x => x.Day)
.ToList();
The problem is that the list items in the double list is being summed up each other into a single double value. The correct result is that double lists should be adding up each other into a single double list for each day. Is there a way to achieve this? Thanks.
If I understood you correctly, here is the code:
var result = Alldatas
.AsEnumerable()
.GroupBy(r => r.TimeStamp.Day)
.Select(x => new {
Day = x.Key,
// Using Aggregate method
Value = x
.Select(y => y.Value)
.Aggregate(new List<double>(), (acc, list) =>
{
for (int i = 0; i < list.Count; ++i)
{
if (acc.Count == i) acc.Add(0);
acc[i] += list[i];
}
return acc;
}),
// Pure LINQ, using GroupBy
Value2 = x
// Create tuple (index, value) for each double
.SelectMany(y => y.Value.Select((z, i) => Tuple.Create(i, z)))
// Group by index
.GroupBy(y => y.Item1)
// Sum values within groups
.Select(y => y.Select(z => z.Item2).Sum())
// Make list
.ToList()
})
.OrderBy(x => x.Day)
.ToList();
For input:
var Alldatas = new []
{
new { TimeStamp = DateTime.Now, Value = new List<double> { 1, 2, 3 } },
new { TimeStamp = DateTime.Now, Value = new List<double> { 1, 2, 3 } },
new { TimeStamp = DateTime.Now, Value = new List<double> { 1, 2, 3 } }
};
This will produce following result:
new[] {
new { Day = 20, Value = new[] {3,6,9}, Value2 = new[] {3,6,9} }
}

Evenly distribute males and females in list with LINQ

I have Class1 like:
{
string Name,
string Sex
}
And I have a List<Class1> with 100 items where 50 are Males and 50 are Females, how do I get 10 groups of 5Males and 5Females each with LINQ?
I already manage to get the list grouped in 10 groups but not distributed evenly by sex.
var foo = My100List.Select((person, index) => new {person, index})
.GroupBy(x => x.index%10)
.Select(i => new Group
{
Name= "Group" + i.Key,
Persons= i.Select(y => y.person).ToList()
});
The code above don't distribute by sex.
Try this (untested):
int groupSize = 5;
var foo = My100List.GroupBy(x => x.Sex)
.SelectMany(g => g.Select((x, i) => new { Person = x, Group = i / groupSize}))
.GroupBy(x => x.Group)
.Select(g => new Group
{
Name = "Group" + g.Key,
Persons = g.Select(x => x.Person).ToList()
});
EDIT
Tested and confirmed. The above code works.
Add .OrderBy for sex before the .Select
Tested and working:
var foo = My100List.OrderBy(p => p.Sex).Select((person, index) => new {person, index})
.GroupBy(x => x.index%10)
.Select(i => new Group
{
Name= "Group" + i.Key,
Persons= i.Select(y => y.person).ToList()
});

how to get a SUM in Linq?

I need to do the following, I have a List with a class which contains 2 integer id and count
Now I want to do the following linq query:
get the sum of the count for each id
but there can be items with the same id, so it should be summerized e.g.:
id=1, count=12
id=2, count=1
id=1, count=2
sould be:
id=1 -> sum 14
id=2 -> sum 1
how to do this?
Group the items by Id and then sum the Counts in each group:
var result = items.GroupBy(x => x.Id)
.Select(g => new { Id = g.Key, Sum = g.Sum(x => x.Count) });
Try it ,
.GroupBy(x => x.id)
.Select(n => n.Sum(m => m.count));
The following program...
struct Item {
public int Id;
public int Count;
}
class Program {
static void Main(string[] args) {
var items = new [] {
new Item { Id = 1, Count = 12 },
new Item { Id = 2, Count = 1 },
new Item { Id = 1, Count = 2 }
};
var results =
from item in items
group item by item.Id
into g
select new { Id = g.Key, Count = g.Sum(item => item.Count) };
foreach (var result in results) {
Console.Write(result.Id);
Console.Write("\t");
Console.WriteLine(result.Count);
}
}
}
...prints:
1 14
2 1

C# LINQ: Get items with max price

I have a list of my objects:
class MyObj
{
public String Title { get; set; }
public Decimal Price { get; set; }
public String OtherData { get; set; }
}
var list = new List<MyObj> {
new MyObj { Title = "AAA", Price = 20, OtherData = "Z1" },
new MyObj { Title = "BBB", Price = 20, OtherData = "Z2" },
new MyObj { Title = "AAA", Price = 30, OtherData = "Z5" },
new MyObj { Title = "BBB", Price = 10, OtherData = "Z10" },
new MyObj { Title = "CCC", Price = 99, OtherData = "ZZ" }
};
What is the best way to get list with unique Title and MAX(Price).
Resulting list needs to be:
var ret = new List<MyObj> {
new MyObj { Title = "BBB", Price = 20, OtherData = "Z2" },
new MyObj { Title = "AAA", Price = 30, OtherData = "Z5" },
new MyObj { Title = "CCC", Price = 99, OtherData = "ZZ" }
};
Well, you could do:
var query = list.GroupBy(x => x.Title)
.Select(group =>
{
decimal maxPrice = group.Max(x => x.Price);
return group.Where(x => x.Price == maxPrice)
.First();
};
If you need LINQ to SQL (where you can't use statement lambdas) you could use:
var query = list.GroupBy(x => x.Title)
.Select(group => group.Where(x => x.Price == group.Max(y => y.Price))
.First());
Note that in LINQ to Objects that would be less efficient as in each iteration of Where, it would recompute the maximum price.
Adjust the .First() part if you want to be able return more than one item with a given name if they both have the same price.
Within LINQ to Objects you could also use MoreLINQ's MaxBy method:
var query = list.GroupBy(x => x.Title)
.Select(group => group.MaxBy(x => x.Price));
var ret = list.GroupBy(x => x.Title)
.Select(g => g.Aggregate((a, x) => (x.Price > a.Price) ? x : a));
(And if you need the results to be a List<T> rather than an IEnumerable<T> sequence then just tag a ToList call onto the end.)
var ret = list.OrderByDescending(x => x.Price).GroupBy(x => x.Title).Select(#group => #group.ElementAt(0)).ToList();
this should do it.
Would like to mention that
var query = list.GroupBy(x => x.Title)
.Select(group => group.Where(x => x.Price == group.Max(y => y.Price))
.First());
Should be
var query = list.GroupBy(x => x.Title)
.First(group => group.Where(x => x.Price == group.Max(y => y.Price)));
I like the Richard solution to greatest-n-per-group problem.
var query = list
.OrderByDescending(o => o.Price) //set ordering
.GroupBy(o => o.Title) //set group by
.Select(o => o.First()); //take the max element
However it needs to be slightly modified
var query = list
.OrderByDescending(o => o.Price) //set ordering
.GroupBy(o => o.Title) //set group by
.Select(o => o.Where(k => k.Price == o.First().Price)) //take max elements

Categories