GroupBy .Key printing literal Index - c#

I am sorting a list of strings by the order of another list of strings
List<string> lstCategories = new List<string>() { "string1", "string2", "string3"};
var groups = lstActivity.OrderBy(x => x.codeAC.Text).ThenBy(x => x.text).GroupBy(x => lstCategories.IndexOf(x.codeAC.Text));
foreach(var group in groups)
{
var slg = new SelectListGroup() { Name = group.Key.ToString() }; //problem is here
foreach(codeAC activity in group)
{
SelectListItem item = new SelectListItem() { Text = activity.text, Value = activity.ID.ToString(), Group = slg };
lstAssignments.Add(item);
}
}
The output of this is:
- 2 // I need this to be the text & not the index position
* text1
* text2
- 1
* text3
* text4
- 3
* text5
* text6
// so on and so on
The group is the literal index position of the list lstCategories.. How do I get it to Select the text and use that as the group and not the index position?
Thank you.

You are mixing grouping with ordering. After grouping you can order groups as well as group elements as you wish. In your sample not only the group key is not what you want, but also the result is not ordered (2, 1, 3).
Here is the query that does what you want:
var groups = lstActivity.OrderBy(x => x.codeAC.Text).ThenBy(x => x.text)
.GroupBy(x => x.codeAC.Text)
.OrderBy(g => lstCategories.IndexOf(g.Key));
and the of course use simply
var slg = new SelectListGroup() { Name = group.Key };

Related

Listing after implementing ranking skipping numbers

I am trying to achieve ranking functionality as below:
Name Points rank
ram 9 1
kamal 9 1
preet 8 2
lucky 7 3
kishan 6.5 4
devansh 6 5
neha 6 5
I have used below code to achieve this:
finalResult = finalResult.OrderByDescending(i => i.points).ThenBy(i => i.academy).ToList();
finalResult = finalResult.AsEnumerable() // Client-side from here on
.Select((player, index) => new RankingEntity()
{
competitorid = player.competitorid,
firstname = player.firstname,
lastname = player.lastname,
academy = player.academy,
points = player.points,
place = player.place,
eventId = player.eventId,
eventname = player.eventname,
categoryname = player.categoryname,
Rank = index + 1
}).ToList();
var t = (from i in finalResult
let rank = finalResult.First(x => x.points == i.points)
select new
{
Col1 = i,
Rank = rank.Rank
}).ToList();
List<RankingEntity> ttt = new List<RankingEntity>();
foreach (var item in t)
{
var a = item.Col1;
var row = new RankingEntity();
row.competitorid = a.competitorid;
row.firstname = a.firstname;
row.lastname = a.lastname;
row.academy = a.academy;
row.points = a.points;
row.place = a.place;
row.eventId = a.eventId;
row.eventname = a.eventname;
row.categoryname = a.categoryname;
row.Rank = item.Rank;
ttt.Add(row);
}
And i am getting result like below:
Please help what i am doing wrong.
What you are trying to achieve is a ranking of a "group" so group the results by the points and then order the groups. For each item in the group give the same rank.
finalResult.GroupBy(item => item.Points) // Group by points
.OrderDescendingBy(g => g.Key) // Order the groups
.Select((g, index) => new { Data = g, GroupRank = index + 1}) // Rank each group
.SelectMany(g => g.Data.Select(item => new RankingEntity
{
/* properties of each item */
Rank = g.GroupIndex
}); // Flatten groups and set for each item the group's ranking
The problem in your method is that you give the ranking for individual items and not the group. Then when you retrieve the rank for the group (from i in finalResult let rank = finalResult.First(x => x.points == i.points)...) you actually set for each item in the group the ranking of one of the elements in it. Therefore, if you first got the last item of the group - that will be the Rank value of each item in it.
Also notice that in the first line of your code you use ToList. Therefore there is not need to use AsEnumerable in the line under it - it is already a materialized in memory collection.

Linq group by bahaving strangely

I have a simple list of int with three elements, all of them are set to 1000.
If I group this list by values, I still get three elements instead of just one.
Why?
var l = new List<int> {1000, 1000, 1000};
var gr = from i in l
group i by new
{
j = i
}
into g1
from g in g1
select new
{
Id = g1.Key.j
};
var count = gr.Count(); // <- count is 3!
Loose the second from
var l = new List<int> {1000, 1000, 1000};
var gr = from i in l
group i by new
{
j = i
}
into g1
select new
{
Id = g1.Key.j
};
var count = gr.Count(); // <- count is 1!
That's because you are again projecting the grouped items. It is returning IEnumerable<IGrouping<int,int>> and you are enumerating IEnumerable<int> with from g in g1 which means you will get the items which are in group 1000 and thus the count 3.
Following query will give you correct result:-
var gr = from i in l
group i by i
into g1
select new
{
Id = g1.Key
};
gr.Count() will be 1 here since we are projecting the Key and not the items inside that group.
With Method Syntax:
You currect query is : l.GroupBy(x => x).SelectMany(x => x) so it will project all the items in the group thus Count 3.
If you want to count the Key then: l.GroupBy(x => x).Select(x => x.Key) this will return 1 since it will create a single group of 1000.
I think you're trying to achieve this:
var l = new List<pairs>
{
new pairs {Index = 0, Value = 1000},
new pairs {Index = 1, Value = 1000},
new pairs {Index = 2, Value = 1000},
};
var gr = l.GroupBy(a => a.Value);
var count = gr.Count(); // <- count is 1
Where Pairs is a simple POCO:
internal class pairs
{
public int Value { get; set; }
public int Index { get; set; }
}
The group by clause gives you the key along with all of the items that are in the group. You are selecting the items and not the key.
For example, try this:
var gr = from i in l
group i by i into g1
select g1;
var count = gr.Count();
var itemCount = gr.First().Count();

LINQ sum not performed when values are identical

I need to sum elements of same type starting from 2 LINQ queries.
Below is my code:
var query1 = from d in _contextProvider.Context.Documents
where d.TransportId == transportId
group d by d.Type
into dg
select new { DocumentType = dg.Key.ToString(), DocumentCount = dg.Count() };
var query2 = from n in _contextProvider.Context.NotificationDocuments
where n.TransportId == transportId
group n by n.TransportId
into nd
select new { DocumentType = "Notification", DocumentCount = nd.Count() };
var query_collapsed = query1.Union(query2)
.GroupBy(p => new { DocumentType = p.DocumentType })
.Select(g => new DocumentCounters() { DocumentType = g.Key.DocumentType, DocumentCount = g.Sum(p => p.DocumentCount) });
Example: below let's analyse values for DocumentType equals to Notification.
Values of query1:
Values of query2:
The collapsed query :
That's correct: 1 + 2 = 3
The problem: I noticed that whenever the count for Notification in query1 is equals to the count for Notification in query2, then the sum is not performed.
Example:
2 + 2 = 2
or
3 + 3 = 3
Any ideas ?
LINQ Union will remove duplicate entries. If you want to merge the two sequences you can use Concat like so:
var query_collapsed = query1.Concat(query2)
.GroupBy(p => new { DocumentType = p.DocumentType })
.Select(g => new DocumentCounters() { DocumentType = g.Key.DocumentType, DocumentCount = g.Sum(p => p.DocumentCount) });

Get item index of a list within Select statement

I need to index the item list with its position after grouping
var result = from i in items
group i by i.name into g
select new { groupname = g.Key,
index = //need to get the index of the item
};
How to get the item index of a list using linq/lambda?
I'm not 100% sure what you're trying to achieve, but I would definitely advice to use methods instead of syntax-based query.
var results = items.GroupBy(x => x.name)
.Select((g, i) => new { product = g.Key, index = i });
Or if you'd like to get indexes from source lift for all items within every group:
var results = items.Select((x, i) => new { x, i })
.GroupBy(x => x.x.name)
.Select(g => new {
product = g.Key,
indexes = g.Select(x => x.i).ToList()
});
var idx = 0;
var result = from i in items
group i by i.name into g
select new { product = g.Key,
index = idx++
};

Search list and order List by value max found c#

i like to confess that i am weak in LINQ.
i have list with data. i want to search list fist by given value and then sort data by max occurance means which comes maximum time in rows.
List<SearchResult> list = new List<SearchResult>() {
new SearchResult(){ID=1,Title="Cat"},
new SearchResult(){ID=2,Title="dog"},
new SearchResult(){ID=3,Title="Tiger"},
new SearchResult(){ID=4,Title="Cat"},
new SearchResult(){ID=5,Title="cat"},
new SearchResult(){ID=6,Title="dog"},
};
if i search & sort list with data like "dog cat" then output will be like
ID=1,Title=Cat
ID=4,Title=Cat
ID=5,Title=Cat
ID=2,Title=dog
ID=6,Title=dog
all cat will come first because this cat keyword found maximum time in all the rows and then dog found maximum time.
this below data will not come because it is not in search term
ID=3,Title=Tiger
looking for solution. thanks
UPDATE PORTION CODE
List<SearchResult> list = new List<SearchResult>() {
new SearchResult(){ID=1,Title="Geo Prism 1995 - ABS #16213899"},
new SearchResult(){ID=2,Title="Excavator JCB - ECU P/N: 728/35700"},
new SearchResult(){ID=3,Title="Geo Prism 1995 - ABS #16213899"},
new SearchResult(){ID=4,Title="JCB Excavator - ECU P/N: 728/35700"},
new SearchResult(){ID=5,Title="Geo Prism 1995 - ABS #16213899"},
new SearchResult(){ID=6,Title="dog"},
};
var to_search = new[] { "Geo", "JCB" };
var result = list.Where(sr => to_search.Any(ts => String.Compare(ts, sr.Title, StringComparison.OrdinalIgnoreCase) == 0))
.GroupBy(sr => sr.Title.ToLower())
.OrderByDescending(g => g.Count());
var matched = result.SelectMany(m => m);
var completeList = matched.Concat(list.Except(matched));
dataGridView2.DataSource = completeList.ToList();
i try to your logic in another apps but it is not working. according to logic three rows first come with GEO keyword and then next 2 rows comes with JCB and then unmatched rest comes. what i need to change in ur code. please help. thanks
This will filter your list and group it by Title, sorting the groups by their size.
List<SearchResult> list = new List<SearchResult>() {
new SearchResult(){ID=1,Title="Cat"},
new SearchResult(){ID=2,Title="dog"},
new SearchResult(){ID=3,Title="Tiger"},
new SearchResult(){ID=4,Title="Cat"},
new SearchResult(){ID=5,Title="cat"},
new SearchResult(){ID=6,Title="dog"},
};
var to_search = new[] { "cat", "dog" };
var result = list.Where(sr => to_search.Any(ts => String.Compare(ts, sr.Title, StringComparison.OrdinalIgnoreCase) == 0))
.GroupBy(sr => sr.Title.ToLower())
.OrderByDescending(g => g.Count());
foreach (var group in result)
foreach (var element in group)
Debug.WriteLine(String.Format("ID={0},Title={1}", element.ID, element.Title));
Output:
ID=1,Title=Cat
ID=4,Title=Cat
ID=5,Title=cat
ID=2,Title=dog
ID=6,Title=dog
If you don't care about the actual grouping, just can flatten the list of groups with SelectMany.
(Note that this code will ignore the case of Title. I don't know if this is what you want or if it is a typo in code: you are using cat and Cat, and in your output it is only Cat, but dog is not capitalized.)
Edit:
To get the unmatched items, you can use Except:
var unmatched = list.Except(result.SelectMany(m => m)); // beware! contains the tiger!
Edit 2:
var result = list.Where(sr => to_search.Any(ts => String.Compare(ts, sr.Title, StringComparison.OrdinalIgnoreCase) == 0))
.GroupBy(sr => sr.Title.ToLower())
.OrderByDescending(g => g.Count());
var matched = result.SelectMany(m => m);
var completeList = matched.Concat(list.Except(matched));
foreach (var element in completeList)
Debug.WriteLine(String.Format("ID={0},Title={1}", element.ID, element.Title));
Output
ID=1,Title=Cat
ID=4,Title=Cat
ID=5,Title=cat
ID=2,Title=dog
ID=6,Title=dog
ID=3,Title=Tiger
Edit 3
var result = from searchResult in list
let key_string = to_search.FirstOrDefault(ts => searchResult.Title.ToLower().Contains(ts.ToLower()))
group searchResult by key_string into Group
orderby Group.Count() descending
select Group;
IEnumerable<string> pets = new[] { "Cat", "dog", "Tiger", "Cat", "cat", "dog" };
var test = pets
.Where(p=>p.ToUpperInvariant() == "CAT" || p.ToUpperInvariant() == "DOG")
.GroupBy(p => p.ToUpperInvariant())
.OrderByDescending(g => g.Count())
.SelectMany(p => p);
foreach (string pet in test)
Console.WriteLine(pet);
I think the following should do the trick.
var searchText = "cat dog";
var searchResult = list
.Select(i => new {
Item = i,
Count = list.Count(x => string.Compare(x.Title, i.Title, true) == 0) // Add counter
})
.Where(i => searchText.Contains(i.Item.Title))
.OrderByDescending(i => i.Count)
.ThenBy(i => i.Item.ID)
.ToList()
Update
If you want unmatched data at the end, you need to add another sorting property in the anonymous object.
var searchText = "cat dog";
var searchResult = list
.Select(i => new {
Item = i,
Matched = searchText.Contains(i.Item.Title.ToUpper()),
Count = list.Count(x => string.Compare(x.Title, i.Title, true) == 0) // Add counter
})
.OrderByDescending(i => i.Matched)
.ThenBy(i => i.Count)
.ThenBy(i => i.Item.ID)
.ToList()

Categories