How to write LINQ query as one query? - linq

I have the following query that groups locations and the average item cost and I would like to write it as one query but I cannot figure out the syntax. What LINQ do I need to do this? I have tried writing it different ways but the syntax is not correct.
var joinedData =
from r in shops
join i in items on r.shopId equals i.shopId
select new
{
Region = r.Location,
ItemCost = i.ItemCost
};
var AverageCostByLocation = joinedData
.GroupBy(m => new { m.Location})
.Select(m => new
{
Location= m.Key.Location,
AverageItemCost = m.Average(x => x.ItemCost)
});

Well, if you put first expression in parenthesis it should allow to join both expressions as they are. Also I'd probably get rid of second anonymous type for perfomance reasons (the new { m.Location} line is redundant, you might want to use .Key instead) :
var AverageCostByLocation =
(from r in shops
join i in items on r.shopId equals i.shopId
select new
{
Region = r.Location,
ItemCost = i.ItemCost
})
.GroupBy(m => m.Location)
.Select(m => new
{
Location= m.Key,
AverageItemCost = m.Average(x => x.ItemCost)
});

Related

How to order items within the group of anonymous type?

I would like to get a result of Linq query as groups of anonymous objects. The items within the groups should be ordered by ID field. I can reach this partly by lambda syntax, but can't get an anonymous objects as result. So I need part of each example.
Executable code: https://dotnetfiddle.net/cPJUN9
var res_g = (from dg in list
group new { dg.ID, dg.IDOperation, dg.IDDiagnosis } by dg.IDOperation
into dg_group
select dg_group);
lambda syntax
var res_g = list
.GroupBy(x => x.IDOperation)
.Select(x => x.OrderBy(x => x.ID)); // order dg by ID asc within group
Alas, you didn't describe exactly your requirements where, only that you want some anonymous type and that they should be ordered by Id. Your query syntax makes different groups than your method syntax. So I can only give an example to create your sequence of anonymous objects
So you have a sequence of similar items, where every item has at least properties Id and IdOperation. You want to make groups of items where every item in each group has the same value for IdOperation. You want to order the elements in each group by ascending Id, and create some anonymous type.
You didn't specify what you want in your anonymous object (after all: your code doesn't do what you want, so I can't deduct it from your code)
Whenever I use GroupBy, and I want to specify the elements of each group, I use the overload of GroupBy that has a parameter resultSelector. With the resultSelector I can precisely define the elements of the group. (The link refers to IQueryable, there is also an IEnumerable version)
IEnumerable<Operations> operations = ... // = your list
// Make Groups of Operations that have the same value for IdOperation
var result = operations.GroupBy(operation => operation.IdOperation,
// parameter resultSelector: take the key (=idOperation) and all Operations that have
// this idOperation, to make one new.
(idOperation, operationsWithThisId) => new
{
// do you need the common idOperation?
IdOperation = idOperation,
// Order the elements in each group by Id:
Operations = operationsWithThisId.OrderBy(operation => operation.Id)
.Select(operation => new
{
// Select only the operation properties that you plan to use
Id = operation.Id,
Name = operation.Name,
StartDate = operation.StartDate,
...
})
.ToList(),
});
In words: from your sequence of Operations, make groups of Operations that have the same value for IdOperation. Then take this common IdOperation, and all Operations that are in this group, to make one anonymous object: this is the anonymous object that you were talking about. So per group, you make one anonymous object.
IdOperation is the value that all Operations in this group have in common
Operations is a list. All Operations in this group are ordered by ascending Id. Several properties are Selected and the result is put in a List.
If you want to group differently, like in you query syntax, simply change parameter keySelector:
var result = operations.GroupBy(operation => new
{
Id = operation.ID,
IdOperation = operation.IDOperation,
IdDiagnosis = operation.IDDiagnosis
},
Although this corresponds with what you did in your query syntax, you will have groups of Operations that have same value for Id / IdOperation / IDDiagnosis. It will be useless to sort the elements in the group by Id, because all Ids in this group will be equal.
Conclusion
With parameter resultSelector you can define the result exactly as you want: the result is not an IEnumerable<IGrouping<Tkey, TElement>>, but an IEnumerable<TResult>.
The TResult is one object created from all elements in one group and the common group value.
you can update GetDict function inner loop to use orderby on group elements
public static void GetDict(List<Operation> list)
{
var res_g = (from dg in list
group new { dg.ID, dg.IDOperation, dg.IDDiagnosis } by dg.IDOperation
into dg_group
select dg_group);
foreach (var x in res_g)
{
Console.WriteLine("Key: " + x.Key);
foreach (var y in x.OrderBy(o=>o.ID))
{
Console.WriteLine("{0} {1} {2}", y.ID, y.IDOperation, y.IDDiagnosis); ;
}
}
}
I hope this solve the issue.
Depending on your Requirements EDIT but I'm Tuning the result set
var res_g_2 = list.Select( p=> new { p.ID, p.IDOperation, p.IDDiagnosis }) .GroupBy(g => g.IDOperation)
.Select(g => new {
IDOperation = g.Key,
Records = g.OrderBy(group => group.ID)
});
Edit full version
public static void GetDict(List<Operation> list)
{
var res_g = (from dg in list
group new { dg.ID, dg.IDOperation, dg.IDDiagnosis } by dg.IDOperation
into dg_group
select new
{
Key = dg_group.Key,
Records = dg_group.OrderBy(g => g.ID)
}) ;
var res_g_2 = list.Select( p=> new { p.ID, p.IDOperation, p.IDDiagnosis }) .GroupBy(g => g.IDOperation)
.Select(g => new {
Key = g.Key,
Records = g.OrderBy(group => group.ID)
});
// you can use either res_g or res_g_2 both give the same results
foreach (var x in res_g)
{
Console.WriteLine("Key: " + x.Key);
foreach (var y in x.Records)
{
Console.WriteLine("{0} {1} {2}", y.ID, y.IDOperation, y.IDDiagnosis); ;
}
}
}

LINQ group by ID - Distinct by order

I have data in a generic list named "Assignment" as shown in the Original table. Want to group by ID and display only one record based on US-UK-India order.
The result should transform from first table to second as shown in the attached image.
Try this:
SQL like syntax.
from a in Assignments
group a by a.Id into gr select gr.FirstOrDefault()
Lambda expression:
Assignments
.GroupBy (a => a.Id)
.Select (gr => gr.FirstOrDefault ())
Try this, its works. I hope this is what you need.
var query01 = context.FirstTable
.GroupBy(p => p.ID
,(ky, el) => new { ky.Value , FirstCountry = el.Select(p => Country).Take(1)});
var Result = query01
.Join(context.FirstTable
, ul => new
{
Id = ul.Value
,Country = ul.FirstCountry.FirstOrDefault()
}
, fl => new
{
Id = (int)fl.ID
, Country = fl.Country
}
, (ul, fl) => new { fl }).ToList();

How does one use .ToList() Inside of a Linq Query?

I've got a class that contains a list item. I would like for a linq query to populate the class, including this list. Here is my query:
var query = from c in context.Cars
select new CarListItem()
{
ID = c.ID,
Make = c.Make,
AvailableColors = context.CarColors.Where(u => u.CarID == c.ID).ToList()
};
Basically, I want to get a list of all of the cars, including a list of the available colors for each respective car.
The problem is that the inclusion of .ToList() within the query results in an error: An error occurred:
LINQ to Entities does not recognize the method 'System.Collections.Generic.List`1[CarSystem.Models.CarColors] ToList[CarColors](System.Collections.Generic.IEnumerable`1[CarSystem.Models.CarColors])' method, and this method cannot be translated into a store expression.
At this point, I don't know whether I am just using wrong syntax within the Linq query (should I use something other than .ToList()?) or if maybe the architecture of the models is wrong.
You can't. EF tries to translate ToList() to SQL and doesn't know how.
You could project to another type, then call ToList():
var query = (from c in context.Cars
select new
{
ID = c.ID,
Make = c.Make,
AvailableColors = context.CarColors.Where(u => u.CarID == c.ID)
}).ToList()
.Select(c => new CarListItem()
{
ID = c.ID,
Make = c.Make,
AvailableColors = c.AvailableColors.ToList()
});
or change the type of CarListItem.AvailableColors to IEnumerable<CarColor>:
var query = from c in context.Cars
select new CarListItem()
{
ID = c.ID,
Make = c.Make,
AvailableColors = context.CarColors.Where(u => u.CarID == c.ID)
};

Linq select subquery

As the title states, I'm trying to perform a select subquery in Linq-To-SQL. Here's my situation:
I have a database view which returns the following fields:
SourceId
LicenseId
LicenseName
CharacteristicId
CharacteristicName
Now I want to be able to store this in a model of mine which has the following properties
Id
Name
Characteristics (this is List which has Id, Name and Icon => Icon is byte[])
Here's the query I wrote which doesn't work:
var licensesWithCharacteristics =
_vwAllLicensesWithAttributesAndSourceIdRepository.GetAll()
.Where(x => x.SourceID == sourceId)
.Select(a => new LicenseWithCharacteristicsModel()
{
LicenseId = a.LicenseId,
LicenseName = a.LicenseName
,CharacteristicList = _vwAllLicensesWithAttributesAndSourceIdRepository.GetAll()
.Where(x => x.LicenseId == a.LicenseId)
.Select(c => new CharacteristicModel { Id = c.CharacteristicID, Name = c.CharacteristicName, Icon = c.Icon })
.Distinct().ToList()
})
.Distinct().ToList();
How would you solve this? I'm trying to do this in one query to keep my performance up, but I'm kind of stuck.
Your sample query and models are not that coherent (where does Icon come from, Characteristics or CharacteristicList), but anyway.
I do this in two parts, you can of course regroup this in one query.
I enumerate the result after the grouping, you may try to do without enumerating (all in linq to sql, but not sure it will work).
var groupedResult =
_vwAllLicensesWithAttributesAndSourceIdRepository.GetAll()
.Where(x => x.SourceID == sourceId)
.GroupBy(m => new {m.LicenseId, m.LicenseName})
.ToList();
var results = groupedResult.Select(group => new LicenseWithCharacteristicsModel {
LicenseId = group.Key.LicenseId,
LicenseName = group.Key.LicenseName,
Characteristics = group.Select(m=> new CharacteristicModel {
Id = m.CharacteristicId,
Name = m.CharacteristicName
}).ToList()
});
in "single query"
_vwAllLicensesWithAttributesAndSourceIdRepository.GetAll()
.Where(x => x.SourceID == sourceId)
.GroupBy(m => new {m.LicenseId, m.LicenseName})
.Select(group =>
new LicenseWithCharacteristicsModel
{
LicenseId = group.Key.LicenseId,
LicenseName = group.Key.LicenseName,
Characteristics = group.Select(m =>
new CharacteristicModel
{
Id = m.CharacteristicId,
Name = m.CharacteristicName
}).ToList()
});

Selecting related pairs LINQ

I'm using LINQ to manipulate a datatable. I have 3 columns - I would like group by one and then select the remaining 2 columns together. At the moment I have something like this
var query = reportDataTable.AsEnumerable()
.GroupBy(c => c["Code"])
.Select(g =>
new {
Code = g.Key,
Rank = g.Select(f => new
{ f["rank"],
f["Name"]}).ToArray()
});
but I get issues due to anonymous types. I know this syntax would work if I could reference the the column headers directly (in say a list or w/e). How can I get around this with DataTables? Cheers.
Edit:
Well I'd like to be able to reference the fields later when I come to populate the data into a different datatable:
foreach (var q in query)
{
DataRow df = dp.NewRow();
df["Code"] = q.Code;
foreach (var rank in q.Rank)
{
df[rank.name] = rank.rank;
}
dp.Rows.Add(df);
}
define your Rank fields, Also if you have a class for it, call related class constructor,
you can see this in bellow code, before ToArray.
var query = reportDataTable.AsEnumerable()
.GroupBy(c => c["Code"])
.Select(g =>
new { Code = g.Key, Rank =
g.Select(f => new { rank = f["rank"], name = f["Name"]})
.ToArray() });

Resources