Convert multiple rows into single in datatable - linq

I have a DataTable in C# with two columns
State Region
A 1
A 2
B 3
B 4
What I want to achieve is
State Region
A 1,2
B 3,4
Is this possible in C# with a single LINQ query?
I am not good in LINQ and also don't want to loop through table to achieve that.

var result = dataTable.AsEnumerable()
.GroupBy(row => row.Field<string>("State"))
.Select(g =>
{
var row = dataTable.NewRow();
row.ItemArray = new object[]
{
g.Key,
string.Join(",",
g.Select(r => r.Field<string>("Region")))
};
return row;
}).CopyToDataTable();

Use GroupBy to achieve this as follow....
var groupedData = from b in dataTable.AsEnumerable()
group b by b.Field<string>("State") into g
select new
{
State = g.Key,
Regions = g,
};
Then you can iterate through each group to get Data...
foreach (var g in groupedData)
{
foreach (var w in g.Regions)
{
Console.WriteLine(w);
}
}

Related

How to find all rows of items that have a part in common using LINQ?

I need to return all records (items) that has a part (X) so I can use that in a group or .GroupBy afterwards
Using this summary data:
ItemName PartName
1 A
1 B
2 A
3 C
So Item1 has two parts (A,B), etc...
I need a LINQ query that will
- find all items that have part A (i.e items 1 and 2)
- return all rows for all these items
1 A
1 B
2 A
Notice that the end result returned the row (1 B) because Item1 has PartA and so I need to get back all rows for Item1.
I was looking at something like:
let items = from data in summary where data.PartName == A select new { data.ItemName } // to get all the items I need
But then, now that I have that list I need to use it to get all the rows for all items listed, and I can't seem to figure it out ...
Actual Source Code (for reference):
NOTE:
Recipe = ITEM
Ingredient = PART
(I was just trying to make it simpler)
ViewFullRecipeGrouping = (
from data in ViewRecipeSummary
group data by data.RecipeName into recipeGroup
let fullIngredientGroups = recipeGroup.GroupBy(x => x.IngredientName)
select new ViewFullRecipe()
{
RecipeName = recipeGroup.Key,
RecipeIngredients = (
from ingredientGroup in fullIngredientGroups
select new GroupIngredient()
{
IngredientName = ingredientGroup.Key
}
).ToList(),
ViewGroupRecipes = (
from data in ViewRecipeSummary
// this is where I am looking to add the new logic to define something I can then use within the next select statement that has the right data based on the information I got earlier in this query.
let a = ViewRecipeSummary.GroupBy(x => x.RecipeName)
.Where(g => g.Any(x => x.IngredientName == recipeGroup.Key))
.Select(g => new ViewRecipe()
{
RecipeName = g.Key,
IngredientName = g.Select(x => x.IngredientName)
})
select new GroupRecipe()
{
// use the new stuff here
}).ToList(),
}).ToList();
Any help would be much appreciated.
Thanks,
I believe this does what you want:
var data = /* enumerable containing rows in your table */;
var part = "X";
var items = new HashSet<int>(data
.Where(x => x.PartName == part)
.Select(x => x.ItemName));
var query = data.Where(x => items.Contains(x.ItemName));
If I understand your comment at the end, I believe this also does what you want:
var query = data
.GroupBy(x => x.ItemName)
.Where(g => g.Any(x => x.PartName == part))
.Select(g => new
{
ItemName = g.Key,
PartNames = g.Select(x => x.PartName)
});

Linq to Sql Query - better solution (optimizing)

The following code works, but it's not a nice code. (low performance)
I have a dictionary with value and key.
First i go trough every webcodes who exist. Then i load all participants in a list (where webcode equals the actual webcode in the foreach). After that i add the data (parameter of the webcode and a count of participants to the dictionary).
Guid compID = Guid.Parse(wID);
ChartModel webcodes = new ChartModel();
webcodes.Title = "Webcodes Statistics";
webcodes.Data = new Dictionary<string, int>();
var webcodesData = db.t_Webcode;
foreach (var w in webcodesData)
{
var wData = db.t_Participant.Where(t => t.FK_Competition == compID && t.Webcode == w.Webcode);
if (wData.Count() != 0)
webcodes.Data.Add(w.Parameter, wData.Count());
}
ViewBag.Webcodes = webcodes;
TIA
You need something along these lines:
webcodes.Data = (from w in db.t_Webcode
join p in db.t_Participant on w.Webcode equals p.Webcode
where p.FK_Competition == compID
group w by w.Parameter into g
select new { g.Key, Count = g.Count() }).ToDictionary();
I can't test it but that is the type of query you need.
This will assume that you have relationships defined in your database and that your LINQ to SQL datacontext are aware of them. If not, you will need to join manually on t_Participants from tWebcode.
This should execute in 1 single SQL query, instead of 1 query per row in tWebcode.
var webcodesAndNoOfParticipants =
from webcode in db.tWebcode
// Define number of participants for this webcode
let numberOfParticipants = webcode.t_Participants.Count(participant => participant.FK_Competition == compID)
where numberOfParticipants > 0
select new {
WebcodeParameter = webcode.Parameter,
NoOfParticipants = numberOfParticipants
};
webcodes.Data = webcodesAndNoOfParticipants.ToDictionary(x => x.WebcodeParameter, x => x.NoOfParticipants);

How to join two datasource which return all rows and group by condition?

I have a problem when using LINQ to join two datasource. Two datasource created by a query like :
var A = (from....
group .... into grp
select new
{
Qty = grp.Count(),
Code = grp.Key.Code,
Name = grp.Key.Name
});
var B = (from....
group .... into grp
select new
{
Qty = grp.Count(),
Code = grp.Key.ContCode,
Name = grp.Key.ContName
});
Value of 'A' will be returned like this :
Qty-Code-Name
1-10A-Cont10
1-20B-Cont20
1-30C-Cont30
Value of 'B' will be returned like this :
Qty-Code-Name
1-10A-Cont10
1-20B-Cont20
1-30C-Cont30
1-40D-Cont40
1-50E-Cont50
I want to join A and B (or do somethings) and the result like this (which sum column 'Qty' if they have the same 'Code' and 'Name') :
Qty-Code-Name
2-10A-Cont10
2-20B-Cont20
2-30C-Cont30
1-40D-Cont40
1-50E-Cont50
How can I do it ? Please help me.
Thank you very much !
Concat the two datasources and than group by code and name.
Something like:
var q = from v in A.Concat(B)
group v by new {v.Code,v.Name } into g
select new
{
Qty = g.Sum(a => a.Qty),
CodeName = g.Key.Code,
Name = g.Key.Name
};

Using LINQ to get all the records in a table having the same value in a specific column

I am trying to get all the rows in a table having the same value in a column. I got it working by using group by:
var groupedData = from row in Tab1Model.ExcelGridDataSource.AsEnumerable()
group row by row.Field<string>("A");
foreach (var group in groupedData)
{
if (group.Count() > 1)
{
//select from each group only the DataRows
//having a certain value in a second column
foreach (var dataRow in group)
{
multipleRowsList.Add(dataRow);
}
}
}
I would like to avoid calling foreach ,get only the groups having a count > 1 and then get ONLY
the DataRows that have a second column with a specific value. Thanks!
try this:
var query = from row in excelDataSource
group row by row.Field<string>("A") into g
select new { Value = g.Key, Rows = g };
var nonZeroRows= from q in query
where q.Rows.Count() > 0
select q.Rows;
// at this point you have an enumerable of enumerables of tablerows.
var list = nonZeroRows.Aggregate(Enumerable.Empty<TableRow>(),
(a, b) => a.Concat(b.Where(c => c.Something == true)); // your condition here
Thanks Atanamir! Here is the final code, just wonder if you have any better ways of doing it. the end goal of this is to flag one of the rows that is entered twice.
var groupedData = from row in Tab1Model.ExcelGridDataSource.AsEnumerable()
group row by row.Field<string>("A")
into g
select new {Value = g.Key, Rows = g};
var nonZeroesRows = from q in groupedData
where q.Rows.Count() > 1
select q.Rows;
//at this point you have an enumerable of enumerables of tables rows
var listRows = nonZeroesRows.Aggregate(Enumerable.Empty<DataRow>(),
(a, b) => a.Concat(b.Where(c => c.Field<bool>("Omit Row") == false)));
//grouped them again and get only the last row from the group wiht a count > 1
var doubleRows = from row in listRows
group row by row.Field<string>("A")
into g
where g.Count() > 1
select g.Last();
Or maybe better:
var groupedData = from row in Tab1Model.ExcelGridDataSource.AsEnumerable()
group row by row.Field<string>("A")
into g
where g.Count() > 1
select new {/*Value = g.Key,*/ Rows = g};
//at this point you have an enumerable of enumerables of tables rows
var listRows = groupedData.Aggregate(Enumerable.Empty<DataRow>(),
(a, b) => a.Concat(b.Rows.Where(c => c.Field<bool>("Omit Row") == false)));
//grouped them again and get only the last row from the group wiht a count > 1
var doubleRows = from row in listRows
group row by row.Field<string>("A")
into g
where g.Count() > 1
select g.Last();

Linq extract a count() value from a data object

I have divAssignments that has potential multiple rows by rNI, an official id, according to a compound key of Indictment and booking numbers.
rNI Booking Indictment
12345 954445 10 12345
12345 954445 10 12346
12345 954445 10 12347
So ID has a count of 3 for a single booking number for this rni.
I get lost attempting to generate a count and a group by booking Number:
var moreThen = from dA in divAssignments
select new { dA.rNI, IndictmentCount = dA.indictmentNumber.Count() };
Most of the examples are dealing with static int[] and don't seem to work in my case.
How do I get a group and then a count? If I could put in a having that would be fantastic.
from a t-sql POV I'd use this:
Select rni, bookingNumber, count(*) IndictmentCount
from divAssignments
group by rni, bookingNumber
having count(*) > 0
TIA
How about something like this:
var query = from item in divAssignments
group item by item.rNI into grouping
select new
{
Id = grouping.Key,
Count = grouping.Count()
}
If you're interested in grouping by both the rNI and the booking number, I would change it to this:
var query = from item in divAssignements
group item by new { item.rNI, a.Booking } into grouping
select new
{
Id = grouping.Key,
Count = grouping.Count
};
OR
var query = from item in divAssignments
group item by item into grouping
select new
{
Id = grouping.Key,
Count = grouping.Count()
}
and implement IEquatable on the divAssignment object to support equality comparison. The other option if you'd like is to write an IEqualityComparer instance to do the composite key comparison. Your query could then look like:
var query =
divAssignments
.GroupBy(i => i, new MyCustomEqualityComparer())
.Select(i => new { Key = i.Key, Count = i.Count());
var query =
from dA in divAssignments
group dA by new { dA.rNI, dA.bookingNumber };
foreach(var grp in query)
{
Console.WriteLine("rNI={0}, bookingNumber={1} => Count={2}", grp.Key.rNI, grp.Key.bookingNumber, grp.Count());
}
If you use a Grouping operator in Linq you will get what you need. The code:
var count = from a in divAssignments
group a by new { a.rNI, a.Booking } into b
select b;
will return a collection of IGrouping objects. This will give you the Key (in my example this will be an anonymous type with an rNI and a Booking property) and a collection of the divAssignments that match the key.
Using Method syntax (much easier to read in my opinion):
First group the records, then select a new result for each group that contains the count.
var groups = divAssignments.GroupBy(d => new { d.rNI, d.Booking });
groups.Select(g=> new { g.Key.rNI, g.Key.Booking, IndictmentCount = g.Count() });

Resources