c# group by alphabets - linq

I need to show list of authors group by last name first letter.
e.g.
A
Kim, Ami
Dim, Amaiar
jin, Amairaz
B
Bin, Bom
Kin, Bomo
C
Cin, Ci
Con, Co
....
Could some one please help me what's the best way to solve the above problem?

If you want to group by, use GroupBy, I assumed you want the output to be ordered (OrderBy), Change the GroupBy expression to match your exact requirment:
List<String> names = new List<String>{"Bill", "Mark", "Steve", "Amnon", "Benny"};
foreach(var g in names.GroupBy(name => name.First()).OrderBy(g => g.Key)){
Console.WriteLine(g.Key);
g.OrderBy(name => name).ToList().ForEach(Console.WriteLine);
}
Will output:
A
Amnon
B
Bill
Benny
M
Mark
S
Steve

You can use GroupBy extension method over Linq object to get the desire result.
List<string> firstNames = new List<string>(){ "Ami", "Amaiar","Amiraz","Bom","Bomo","Ci","Co" };
var groups = firstNames.GroupBy(x=>x[0]);
foreach (var element in groups)
{
Console.WriteLine("{0}", element.Key);
foreach (var word in element)
Console.WriteLine(" {0}", word);
}

Related

How to get a list of the grouped values in linq groupby?

Linq newbie here, struggling with my first GroupBy query.
I have a list of objects of type KeywordInstance which represents a keyword, and the ID of the database record to which the keyword was applied.
Keyword RecordID
macrophages 1
macrophages 2
cell cycle 3
map kinase 2
cell cycle 1
What I want is a collection of all keywords, with a list of the RecordIDs to which each keyword was applied.
Keyword RecordIDs
macrophages 1, 2
cell cycle 1, 3
map kinase 2
I tried using Linq to get it into a new object. I only managed to get the distinct keywords.
var keywords = allWords
.GroupBy(w => w.keyword)
.Select(g => new {keyword = g.Key});
The problem is that I can't seem to get the values of g in any way. g is of the type IGrouping<String, KeywordInstance> and by documentation, it only has the property Key, but not even the property Value. All the examples I have seen on the Internet for groupby just tell me to select g itself, but the result of
var keywords = allWords
.GroupBy(w => w.keyword)
.Select(g => new {keyword = g.Key, RecordIDs = g});
is not what I want.
Any try to get something out of g fails with the error message System.Linq.IGropuing<string, UserQuery.KeywordInstance> does not have a definition for [whatever I tried].
What am I doing wrong here?
I think you are close to you solution.
var keywords = allWords
.GroupBy(w => w.keyword)
.Select(g => new
{
keyword = g.Key,
RecordIDs = g.Select(c => c.ID)
});
Just Select the records you need.
The reason you are seeing the Keyword-column as well as the ID-column, is becuase it's part of g
var keywords = allWords.GroupBy(w => w.keyword);
foreach (var itm in keywords)
{
var list = itm.ToList();
//list returns all of the original properties/values objects from allwords.
//itm.key returns w.keyword
}

List.GroupBy<> error using LInq

I'm trying to group a generic List<> in C#. The code compiles, but the application (Silverlight) throws the following error (CharOpps is the class of objects in the list I'm trying to group):
Unhandled Error in Silverlight Application Unable to cast object of type 'Grouping[System.DateTime,Invoc_SalesDashboard.ChartOpps]' to type 'Invoc_SalesDashboard.ChartOpps'.
Here's the code:
var newtemplist = list.GroupBy(opp =>
new DateTime(opp.EstimatedCloseDate.Year, opp.EstimatedCloseDate.Month, 1)).OrderBy(opp => opp.Key);
I've also tried:
var newtemplist =
from opp in list
orderby opp.EstimatedCloseDate
group opp by new { opp.EstimatedCloseYear, opp.EstimatedCloseMonth };
ChartOpps have a revenue value, and the EstimatedCloseDate value. What I'm hoping to end up with is a list of ChartOpps with the revenue aggregated in the year/month groupings.
foreach (ChartOpps c in newtemplist)
{
ErrorBox.Text += "o";
}
You're not showing us what you're doing with the result newtemplist. The runtime error message indicates that you are taking a group and trying to treat it as an instance of ChartOpps which is clearly impossible. Show us that code and we can help you fix it.
Edit:
Okay, now the problem is clear. To enumerate the results of the grouping, you need to proceed as follows:
foreach(var group in newtemplist) {
foreach(ChartOpps c in group) {
// do something with c here
}
}
The result of newtemplist is a sequence of sequences, each sequence having all of its elements having the same value of new DateTime(opp.EstimatedCloseDate.Year, opp.EstimatedCloseDate.Month, 1). Therefore, to enumerate this sequence of sequences, you first have to enumerate the groups, and then within each group enumerate the instances of ChartOpps.
Not knowing anything about your class structure, here is a basic attempt:
List<CharOpps> list = GetList();
var newtemplist =
from opp in list
group opp by opp.EstimatedCloseYear into g
select new { g = g.Key, CharOpps = g };
If you take the var out of the picture, it all becomes clear.
IEnumerable<IGrouping<DateTime, ChartOpps>> newtemplist = list
.GroupBy(opp => new DateTime(
opp.EstimatedCloseDate.Year,
opp.EstimatedCloseDate.Month,
1))
.OrderBy(opp => opp.Key);
foreach (ChartOpps c in newtemplist)
{
ErrorBox.Text += "o";
}
The error occurs in the assignment of the first element of newtemplist to c. c is allowed to reference ChartOpps instances. The first element of newtemplist is a IGrouping<DateTime, ChartOpps>, not a ChartOpps. The implicit cast in the foreach fails and you get a runtime exception.
Try instead:
foreach(IGrouping<DateTime, ChartOpps> g in newtemplist)
{
foreach (ChartOpps c in g)
{
ErrorBox.Text += "o";
}
}

dot notation equivalent for JOIN

string[] names = { "Burke", "Connor", "Frank",
"Albert", "George", "Harris", "David" };
peoples[] people = {
new peoples("Connor",20),
new peoples("John",22),
new peoples("Merry",33),
new peoples("Frank",65),
new peoples("Frank",34),
new peoples("George",19)
};
var query = from n in names
join p in people on n equals p.Name into matching
select new { Name = n, Count = matching.Count() };
Please tell me dot notation of this query.
Thanks.
The dot notation for a join depends on what follows it and whether or not you've got an "into" clause (for a group join). In this case it would be:
var query = names.GroupJoin(people, n => n, p => p.Name,
(n, matching) => new { Name = n, Count = matching.Count() });
If you didn't use "into" it would use Join instead of GroupJoin
If you had anything other than just "select" afterwards, it would introduce a new transparent identifier to keep "(n, matching)" as a tuple, effectively.

Applying Group By in LINQ

I decided to group the collection by length of the string.Need suggestion from you to correct myself.
string[] collection = {"five","four","ten","one"};
var groupedValues =
from w in collection
group w by w.Length into getByGroup
select getByGroup;
foreach (var g in groupedValues)
{
Console.WriteLine(g);
}
The output is :
System.Linq.Lookup....
System.Linq.Lookup....
What went wrong ?
GroupBy returns a Lookup object which contains the Key and the collection in the grouping.
foreach (var g in GroupedValues)
{
Console.WriteLine("There are {1} strings of length {0}.", g.Key, g.Count());
foreach (var v in g)
{
Console.WriteLine(" - {0}", v);
}
}
What went wrong depends on what you wanted to do!
The sequence you get back after grouping is not a flat sequence of the original objects (so in your case, it's not a sequence of strings). Otherwise how would they have been grouped?
Maybe given that you apparently expected a flat list of strings, you actually wanted to order them by length:
var collection = new[] {"five","four","ten","one"};
var byLength = collection.OrderBy(s => s.Length);
foreach (var s in GroupedValues)
Console.WriteLine(s);
Or if you wanted to group them, then you have to deal with each group in turn, and each group is a separate list of strings:
foreach (var g in GroupedValues)
{
Console.WriteLine("Strings of length " + g.Key + ":");
foreach (var s in g)
Console.WriteLine(" " + s);
}

DataTable Query

I am new to LINQ. I am trying to find the rows that does not exists in the second data table.
report_list and benchmark both type are : DataTable. Both these datatables are being populated using OleDbCommand,OleDbDataAdapter. I am getting an error "Specified cast is not valid." in foreach ... loop. I would appreciate your help.
var result = from a in report_list.AsEnumerable()
where !(from b in benchmark.AsEnumerable()
select b.Field<int>("bench_id")
)
.Contains(a.Field<int>("BenchmarkID"))
select a;
foreach (var c in result)
{
Console.WriteLine(c.Field<string>("Name"));
}
I don't know if I understood your question. Are you trying to get the items that exists in the first table but not in the second?
var first = new string[] { "b", "c" };
var second = new string[] { "a", "c" };
//find the itens that exist in "first" but not in "second"
var q = from f in first
where !second.Contains(f)
select f;
foreach (var s in q) {
Console.WriteLine(s);
}
//Prints:
//b
I suggest you to make the inner query first, once it does not depend on the outer record.
From a in report_list
Group Join b in benchmark On a.bench_id Equals b.bench_id Into g = Group
Where g.Count = 0
Select a
Note that this is VB syntax.
My suspicion is that one of the fields you are comparing is not an integer in the database. I believe that the invalid cast exception is being thrown by one of the Field<int>() calls since that is one of the three different exceptions that this method can throw. See docs here.
Perhaps use the .Except() extension to get the set difference of the two sets?
(from b in benchmark.AsEnumerable()
select new { id = b.Field<int>("bench_id")}).Except(
from a in report_list.AsEnumerable()
select new {id = a.Field<int>("BenchmarkID")})
Not actually sure of the precise syntax, but that should work by taking the ids in benchmark, and then removing all equivalent ids in report_list, leaving only the ids that don't match. (I hope this is the order you were after...)
Note: This is also assuming that the above issue mentioned by tvanfosson isn't also a problem

Resources