C# Convert columns to rows using linq? - c#

I have the following table structure. I want to retrieve value corresponding to key for each group name and insert it store it sequentially in a model class
the table data has been read using a ExecuteQuery and stored in a list of class object, in the below example I will have 4 rows returned. I need to convert it into 2 rows, the column values coming in as rows.
I have the following code written now, But is there any other way to verify it without explicitly checking for say Key == "GroupSpecificProperty1"?
If there is a 3rd category added later, I shouldn't have to modify this code
Result = results.GroupBy(p => p.GroupName )
.Select(g => new FinalModel()
{
GroupName = g.Select(p => p.GroupName ).FirstOrDefault(),
GroupSpecificProperty1 = g.Where(q => q.Key == "GroupSpecificProperty1").Select(v => v.Value).Cast<string>().FirstOrDefault(),
GroupSpecificProperty2= g.Where(q => q.Key == "GroupSpecificProperty2").Select(v => v.Value).Cast<string>().FirstOrDefault(),
}).ToList();

results.GroupBy(p => p.GroupName)
.Select(g => new FinalModel
{
GroupName = g.Key,
Properties = g.ToDictionary(item => item.Key, item=> item.Value)
});
And in the case that for a given GroupName the keys are not unique and you'd want to avoid a "key already exists" exception then you can:
results.GroupBy(p => p.GroupName)
.Select(g => new FinalModel
{
GroupName = g.Key,
Properties = g.GroupBy(item => item.key)
.ToDictionary(innerGroup => innerGroup.Key,
innerGroup => innerGroup.Select(innerItem => innerItem.Value))
});
Then of course you can also replace the outer/inner dictionary with a LookUp if it fits your needs better.

Related

Convert Sql to linq with groupby

I have view on which I use this request
Select Spendband, SUM(SpendCurrencyJob), SUM(SpendDocumentCount)
From analysis.vwJobSupplierMetrics
Where JobId = '500E0DD1-E3D3-4887-95EF-01D3C9EA8FD0'
Group by SpendBand
And it's running sucessfully
and get me this data
How I need to write it using linq to get same data?
I tried like this
var data = await _dbContext.VwJobSupplierMetrics.Where(x => x.JobId == jobId)
.GroupBy(x => x.SpendBand)
.Select(x => new HumpChartDto() {SpendBand = x.SpendBand}).ToListAsync();
But on new HumpChartDto() {SpendBand = x.SpendBand} I got Cannot resolve symbol 'SpendBand
How I can solve this?
First, after grouping on SpendBand, you need to access it via Key property. Second, to compute Sum, you can use Sum method.
var data = await _dbContext.VwJobSupplierMetrics.Where(x => x.JobId == jobId)
.GroupBy(x => x.SpendBand)
.Select(x => new HumpChartDto()
{
SpendBand = x.Key,
SumOfSpendCurrencyJob = x.Sum(s => s.SpendCurrencyJob),
SumOfSpendDocumentCount= x.Sum(s => s.SpendDocumentCount),
})
.ToListAsync();
Note - change the property name accordingly for name I've used for SumOfSpendCurrencyJob and SumOfSpendDocumentCount as don't know the definition of HumpChartDto class.

Difficulty with LINQ Query writing custom sort logic

I have files like Avinash_Create.sql, Avinash_Insert.sql, Avinash_Update.sql , Avinash_Delete.sql.
I need to iterate over the files list and group them based on the name and order by create, insert, update, and delete files.
I am finding it difficult difficult to accomplish. This is what I have so far:
var userGroups = shortfilenames.GroupBy(s => s.Substring(0, s.IndexOf('_')))
.Select(g => g.OrderBy(x => x.Substring(x.IndexOf('_')).Contains("CREATE"))
.ThenBy(x => x.Substring(x.IndexOf('_')).Contains("INSERT"))
.ThenBy(x => x.Substring(x.IndexOf('_')).Contains("UPDATE"))
.ThenBy(x => x.Substring(x.IndexOf('_')).Contains("DELETE")));
The above query is grouping by name 'Avinash' but not working for custom ordering. Please help.
Update:
Please see updated query , still it is not sorting properly
Uses Split to extract the relevant sections of the string. Converts the result to upper case, which seems to be missing from your attempt. Additionally makes it a bit shorter by using an array to hold your custom sort order and then Array.IndexOf to get a sort order from it, rather than multiple OrderBy/ThenBy.
var ordering = new [] {"CREATE", "INSERT", "UPDATE", "DELETE"};
var results = shortfilenames.GroupBy(s => s.Split('_')[0])
.Select(g => g.OrderBy(x => Array.IndexOf(ordering, x.Split('_')[1].Split('.')[0].ToUpper())));
This seems to be what what you want:
var shortfilenames = new List<string>(){"Avinash_Create.sql" , "Avinash_Insert.sql" , "Avinash_Update.sql" , "Avinash_Delete.sql"};
var userGroups = shortfilenames
.Select(fn =>
{
string fileName = Path.GetFileNameWithoutExtension(fn);
string[] nameAndAction = fileName.Split('_');
return new
{
extension = Path.GetExtension(fn),
fileName,
name = nameAndAction[0],
action = nameAndAction[1]
};
})
.GroupBy(x => x.name)
.Select(g => g.OrderByDescending(x => x.action.Equals("CREATE", StringComparison.InvariantCultureIgnoreCase))
.ThenByDescending(x => x.action.Equals("INSERT", StringComparison.InvariantCultureIgnoreCase))
.ThenByDescending(x => x.action.Equals("UPDATE", StringComparison.InvariantCultureIgnoreCase))
.ThenByDescending(x => x.action.Equals("DELETE", StringComparison.InvariantCultureIgnoreCase))
.ToList());
foreach (var ug in userGroups)
foreach (var x in ug)
Console.WriteLine("{0} {1}", x.name, x.action);
prints out:
Avinash Create
Avinash Insert
Avinash Update
Avinash Delete
Presumes that the file-names always contain the underscore.

Join an array of string with the result of an existing linq statement

As a follow up to my last question here:
Filtering a list of HtmlElements based on a list of partial ids
I need to take this statement:
doc.All.Cast<HtmlElement>()
.Where(x => x.Id != null)
.Where(x => ids
.Any(id => x.Id.Contains(id))).ToList();
and join it with an array of strings called fields. Assuming the array and list will have the same amount of elements each and line up correctly. I tried using Zip() but thought I might need to use an additional linq statement to make it work.
Assuming that fieldList[0] and IdList[0] corresponding to each other, you can do the following:
var IdList = doc.All.Cast<HtmlElement>()
.Where(x => x.Id != null)
.Where(x => ids
.Any(id => x.Id.Contains(id))).ToList();
var resultList = fieldList
.Select( (item, index) => new { Field = item, Id = IdList[index] })
.ToDictionary(x => x.Id, x => x.Field);
You have mentioned it already, you can use Enumerable.Join:
var joined = from id in fields
join ele in elements on id equals ele.Id
select new { Element = ele, ID = id };
var dict = joined.ToDictionary(x => x.ID, x => x.Element);
I've presumed that you want to join them via ID. I've also presumed that the string[] contains only unique ID's. Otherwise you need to use Distinct.

Query entity and return back a dictionary

I'm trying to query a view (entity) from the database and return back a dictionary. There are duplicates in the view so I tried groupby and I can't figure it out.
var queryresults = db.MyView.Where(x => x.year == myYear)
.GroupBy(g => new { g.myCode, g.myCodeName})
.ToDictionary(d => d.myCode, d => d.myCodeName);
You should group by dictionary key property if you want to avoid duplicate keys error. Then you can select code name of first item in each group as dictionary entry value:
var queryresults =
db.MyView.Where(x => x.year == myYear)
.GroupBy(x => x.myCode)
.ToDictionary(g => g.Key, g => g.First().myCodeName);

Use Linq to return first result for each category

I have a class (ApplicationHistory) with 3 properties:
ApplicantId, ProviderId, ApplicationDate
I return the data from the database into a list, however this contains duplicate ApplicantId/ProviderId keys.
I want to supress the list so that the list only contains the the earliest Application Date for each ApplicantId/ProviderId.
The example below is where I'm currently at, but I'm not sure how to ensure the earliest date is returned.
var supressed = history
.GroupBy(x => new
{
ApplicantId = x.ApplicantId,
ProviderId = x.ProviderId
})
.First();
All advice appreciated.
Recall that each group formed by the GroupBy call is an IGrouping<ApplicationHistory>, which implements IEnumerable<ApplicationHistory>. Read more about IGrouping here. You can order those and pick the first one:
var oldestPerGroup = history
.GroupBy(x => new
{
ApplicantId = x.ApplicantId,
ProviderId = x.ProviderId
})
.Select(g => g.OrderBy(x => x.ApplicationDate).FirstOrDefault());
You are selecting first group. Instead select first item from each group:
var supressed = history
.GroupBy(x => new {
ApplicantId = x.ApplicantId,
ProviderId = x.ProviderId
})
.Select(g => g.OrderBy(x => x.ApplicationDate).First());
Or query syntax (btw you don't need to specify names for anonymous object properties in this case):
var supressed = from h in history
group h by new {
h.ApplicantId,
h.ProviderId
} into g
select g.OrderBy(x => x.ApplicationDate).First();

Categories