With this query:
foreach(var y in items)
{
<div>#y.fieldA => #y.FieldB</div>
}
I get this output:
3 => 2
3 => 1
2 => 2
3 => 2
I know in LINQ we can easily count and group by with this:
foreach(var y in items.GroupBy(g => g.Field)
.Select(group => new {Field = group.Key, count = group.Count()}))
{
<div>#y.Field : #y.count</div>
}
But how can I adapt this to the concatenated output so that I can get this result:
3 => 2 : 2
3 => 1 : 1
2 => 2 : 1
Use Select to project the desired string instead of projecting an anonymous object:
foreach(var item in items.GroupBy(g => $"{g.FieldA} => {g.FieldB}")
.Select(g => $"{g.Key} : {g.Count()}"))
{
<div>#item</div>
}
For a pre C# 6.0 version:
foreach(var item in items.GroupBy(g => g.FieldA + " => " + g.FieldB)
.Select(g => g.Key + " : " + g.Count()))
{
<div>#item</div>
}
Related
I am trying to figure out a way to rank items in a list that has duplicated values.
For example:
QTECDE
RANK
40
1
30
2
24
3
18
4
4
5
4
5
3
6
But my code always skips a number when I have a duplicated rank. This what I get:
QTECDE
RANK
40
1
30
2
24
3
18
4
4
5
4
5
3
7 (7 insted of 6)
Here's my code:
var rankedList = oList.OrderByDescending(p => p.QTECDE)
.Select((p, i) => new { Order = 1 + i, lst = p })
.GroupBy(p => new { p.lst.QTECDE })
.SelectMany(g => g.Select(p => new
{
RANK = g.Min(x => x.Order),
NO_ART = p.lst.NO_ART,
QTECDE = p.lst.QTECDE,
LIB_INDEX_FR_SUP = p.lst.LIB_NIVEAU_SUP_FR,
LIB_IMAGE = p.LIB_IMAGE,
}));
Any solutions?
You just need the index of the group not the items:
var rankedList = oList
.OrderByDescending(p => p.QTECDE)
.GroupBy(p => new { p.QTECDE })
.SelectMany((g, groupIndex) => g
.Select(p => new
{
RANK = groupIndex + 1,
NO_ART = p.NO_ART,
QTECDE = p.QTECDE,
LIB_INDEX_FR_SUP = p.LIB_NIVEAU_SUP_FR,
LIB_IMAGE = p.LIB_IMAGE,
}));
You're determining the rank/order on your source items. You want to apply the (item, index) to your SelectMany() instead of your Select().
In the below code i have a list i am trying to get values from list using linq query and sum the values.But i don't know how to sum the values.So please help me to resolve the issue.
list contains:
Desc Month level value
M1 Jan L1 2
M1 Jan L2 6
M2 Feb L1 4
M2 Feb L2 1
My Expected Result:
M1 Jan 8
M2 Feb 5
var sums1 = objList
.GroupBy(y => new { y.Desc, y.Month, y.value })
.Select(group => new { KeyValue = group.Key, Count = group.Count() });
You shouldn't be including the value in your grouping - you want to group by just Desc and Month (I assume), then sum the value parts. Two options:
var sums1 = objList
.GroupBy(y => new { y.Desc, y.Month })
.Select(group => new { KeyValue = group.Key, Sum = group.Sum(y => y.value) });
Or in a single call:
var sums1 = objList.GroupBy(
y => new { y.Desc, y.Month },
(key, values) => new { key.Desc, key.Month, Sum = values.Sum(y => y.value) });
Replace your group.Count with group.Sum(x=>x.value)
Use LINQ's Sum() extension method for IEnumerable.
.Select(group => new { KeyValue = group.Key, Sum = group.Sum(obj => obj.value)});
My getTrustActivitiesFromStorage List looks something this
venueId venueName activityId
1 Location1 Zumba
2 Location2 Yoga
1 Location1 Yoga
1 Location1 MetaFit
3 Location3 Zumba
Here's the code i use to group etc
List<TrustActivities> filteredVenues = new List<TrustActivities>();
IEnumerable<TrustActivities> groupedVenueCollection = getTrustActivitiesFromStorage
.GroupBy(customer => customer.venueName)
.Select(group => group.First())
.OrderBy(x => x.venueName);
// Loop
foreach (TrustActivities activity in groupedVenueCollection)
{
filteredVenues.Add(new TrustActivities
{
filterId = Convert.ToInt32(activity.venueId),
filterName = activity.venueName,
filterCount = 55
});
}
This successfully groups the list and outputs the 3 matches:
1 Location1 (55)
2 Location2 (55)
3 Location3 (55)
The final bit i need help with is counting each group, so filterCount = 55 will be replace with the dynamic count to give:
1 Location1 (3)
2 Location2 (1)
3 Location3 (1)
can someone show me how to do this?
thanks
You just need group.Count():
var groupedVenueCollection = getTrustActivitiesFromStorage
.GroupBy(customer => customer.venueName)
.OrderBy(g => g.Key);
foreach (var group in groupedVenueCollection)
{
TrustActivities firstActivity = group.First();
filteredVenues.Add(new TrustActivities
{
filterId = Convert.ToInt32(firstActivity.venueId),
filterName = firstActivity.venueName, // or group.Key
filterCount = group.Count() // <--- !!!
});
}
You could also do it in one query without a loop:
List<TrustActivities> filteredVenues = getTrustActivitiesFromStorage
.GroupBy(customer => customer.venueName)
.OrderBy(g => g.Key)
.Select(g => new { Activity = g.First(), Count = g.Count() })
.Select(x => new TrustActivities
{
filterId = Convert.ToInt32(x.Activity.venueId),
filterName = x.Activity.venueName,
filterCount = x.Count
})
.ToList();
Instead of the .Select(g => g.First()), you'd do something like this:
IEnumerable<TrustActivities> groupedVenueCollection = getTrustActivitiesFromStorage
.GroupBy(customer => new { customer.venueId, customer.venueName });
foreach (var activity in groupedVenueCollection)
{
filteredVenues.Add(new TrustActivities
{
filterId = Convert.ToInt32(activity.Key.venueId),
filterName = activity.Key.venueName,
filterCount = activity.Count()
});
}
Also, your variable names are confusing. The table appears to be venues, but you call them customers and activities
I need to rank on list by Linq.
class test
{
public int Id { get; set; }
public string Destination { get; set; }
}
my data is as follows:
ID Destination
1 Miami
2 Miami
3 Boston
4 Atlanta
what i want is this:
ID Destination Value
1 Miami Miami1
2 Miami Miami2
3 Boston Boston1
4 Atlanta Atlanta1
How to get this by Linq??
Try this:
list.GroupBy(l => l.Destination)
.SelectMany(g => g.Select((x,i) => new {
x.Id,
x.Destination,
Value = x.Destination + (i+1)
}));
The i in the SelectMany will give you the index of the item in each group. Just add 1 to get the rank.
Results:
Id destination Value
1 Miami Miami1
2 Miami Miami2
3 Boston Boston1
4 Atlanta Atlanta1
Here's how you could compute the ranks within groups:
var result =
items.GroupBy(d => d.destination)
.SelectMany(g =>
g.Select(d =>
new
{
d.Id,
d.destination,
Value = d.destination +
(g.Count(x => x.Id < d.Id) + 1)
}));
var result = list.GroupBy(x=>x.Destination)
.Select(g => g.Select((x,i) =>
new {
ID = x.ID,
Destination = x.Destination,
Value = x.Destination + (i + 1)
}))
.SelectMany(x=>x);
List<string> str = new List<string>() {
"Alpha", "Beta", "Alpha", "Alpha", "Gamma", "Beta", "XYZ" };
Expected output:
String | Indexes
----------------------------
Alpha | 0, 2, 3
Beta | 1, 5
Gamma and XYZ are distinct so, they are ignored.
I've done this by comparing the strings manually. Would it be possible to do it using LINQ in more easier way?
foreach (var grp in
str.Select((s, i) => new { s, i })
.ToLookup(pair => pair.s, pair => pair.i)
.Where(pair => pair.Count() > 1))
{
Console.WriteLine("{0}: {1}", grp.Key, string.Join(", ", grp));
}
Something like this should work:
var elements = str
.Select((Elem, Idx) => new {Elem, Idx})
.GroupBy(x => x.Elem)
.Where(x => x.Count() > 1);
If you want to get a Dictionary<string,List<int>> having the duplicated string as key and the indexes as value, just add
.ToDictionary(x => x.Key, x => x.Select(e => e.Idx).ToList() );
after Where()
You can get the non-distinct strings by grouping, then you can get the index for each non-distinct string and group them to create an array for each string:
var distinct = new HashSet<string>(
str.GroupBy(s => s)
.Where(g => g.Count() > 1)
.Select(g => g.Key)
);
var index =
str.Select((s, i) => new {
Str = s,
Index = i
})
.Where(s => distinct.Contains(s.Str))
.GroupBy(i => i.Str).Select(g => new {
Str = g.Key,
Index = g.Select(s => s.Index).ToArray()
});
foreach (var i in index) {
Console.WriteLine("{0} : {1}", i.Str, String.Join(", ", i.Index.Select(n => n.ToString())));
}
Output:
Alpha : 0, 2, 3
Beta : 1, 5