Given the following simple object
public class Foo {
public int PrimaryKey;
public int ForeignKey;
public bool FlagOne;
public bool FlagTwo;
}
Suppose I have received a IQueryable<Foo>. Generally, if I want to do a count operation on each flag I would do this:
IQueryable<Foo> foos = GetFoos();
var total = foos.Count();
var flagOneTotal = foos.Count(p => p.FlagOne);
var flagTwoTotal = foos.Count(p => p.FlagTwo);
In EF, the above would execute 3 queries in the database. I would like to retrieve all these in a single query.
For grouping, I can do this to execute single query:
var q = from foo in foos
group foo by foo.ForeignKey into g
select new {
ForeignKey = g.Key,
Total = g.Count(),
FlagOneTotal = g.Count(p => p.FlagOne),
FlagTwoTotal = g.Count(p => p.FlagTwo)
};
var list = q.ToList();
But how would I do the same if I want to get the totals for all elements regardless of foreign key in a single query and a single anonymous object ?
In other words, how would I tell .net that all elements in foos need to be considered 1 group so I can do Count operations on them.
This should do the job:
var q = from foo in foos
group foo by 1 into g
select new {
Total = g.Count(),
FlagOneTotal = g.Count(p => p.FlagOne),
FlagTwoTotal = g.Count(p => p.FlagTwo)
};
var list = q.ToList();
Cheers
Related
This is an already asked question, but that question is to work with 2 attributes only, I need to work with 3 attributes, So I am copy-pasting most of the text.
Let's suppose if we have a class like
class Person {
internal int PersonID;
internal string car ;
internal string friend ;
}
Now I have a list of this class: List persons;
Now this list can have instances multiple same PersonIDs, for ex.
persons[0] = new Person { PersonID = 1, car = "Ferrari" , friend = "Josh" };
persons[1] = new Person { PersonID = 1, car = "BMW" , friend = "Olof" };
persons[2] = new Person { PersonID = 2, car = "Audi" , friend = "Gustaf" };
Is there a way I can group by PersonID and get the list of all the cars and friends he has? For example the expected result would be:
class Result {
int PersonID;
List<string> cars;
List<string> friends;
}
From what I have done so far:
IEnumerable resultsForDisplay = ResultFromSQL_Query.GroupBy(
p => p.PersonId.ToString(),
p => p.car,
(key, g) => new { PersonId = key, car = g.ToList()});
But now I'm stuck at getting the friend's array in resultsForDisplay
Sure, you can perform LINQ queries on the group g as well, like:
IEnumerable<Result> resultsForDisplay = from q in ResultFromSQL_Query
group q by q.PersonID into g
select new Result {PersonID = g.Key,cars = g.Select(x => x.car).ToList(), friends = g.Select(x => x.friend).ToList()};
Or with lambda expressions:
IEnumerable<Result> results = persons.GroupBy(x => x.PersonID)
.Select(g => new Result { PersonID = g.Key, cars = g.Select(x => x.car).ToList(), friends = g.Select(x => x.friend).ToList()};
So you can perform any LINQ query on a group (which thus behaves as an IEnumerable<> on the elements of that grou), like a .Select(..), but also .Sum(..), .Average(..) and other sub queries, aggregates, etc.
I need make union between two LINQ queries, but the second query need have more fields that the first. How can I do it?
Example:
public static void Dummy()
{
var query1 = this.Db.Table1.Select(s => new MyObject() { A = s.Field1, B = s.Field2 });
var query2 = this.Db.Table2.Select(s => new MyObject() { A = s.Field1, B = s.Field2, C = s.Field3 });
var result = query1.Union(query2);
}
When I calls result.ToList(), occurs the following error:
The type 'MyObject' appears in two structurally incompatible
initializations within a single LINQ to Entities query. A type can be
initialized in two places in the same query, but only if the same
properties are set in both places and those properties are set in the
same order.
How Can I resolve this problem?
Obs.: I can't put the Field3 in the query1 (I don't have access to the query one, because this I Can't changed it)
You don't have to put Field3 in first query but Union requires same number of columns and in same order. Specify a dummy value for third column/field C like:
var query1 = this.Db.Table1.Select(s => new MyObject()
{ A = s.Field1, B = s.Field2 , C= ""});
Assign C whatever is the default value of Field3, may be null for reference type and 0 for numbers etc.
If you don't have access to it modify query1 then create a new query using query1 like:
var newQuery = query1.Select(s=> new MyObject()
{ A = A, B = B , C= ""});
and then use that in Union
var result = newQuery.Union(query2);
As-is, you can't. You can only union 2 sets that have the same structure. If you don't mind modifying query1, however:
var query1 = this.Db.Table1.Select(s => new MyObject()
{ A = s.Field1, B = s.Field2, C = null });
This would allow them to union properly, as they have the same structure.
You can do it, like this:
Create a object devired from MyObject
class MyObjectUnion : MyObject{
}
So, the method goes like this:
public static void Dummy()
{
var query1 = this.Db.Table1.Select(s => new MyObject() { A = s.Field1, B = s.Field2 });
var query1modified = this.Db.Table2.Select(s => new MyObjectUnion() { A = s.Field1, B = s.Field2, C = null });
var query2 = this.Db.Table2.Select(s => new MyObjectUnion() { A = s.Field1, B = s.Field2, C = s.Field3 });
var result = query1modified.Union(query2);
}
It works
Because records in query1 will never have a property "C", and all records in query2 will have a property "C", it is unlikely that a record in query1 will be equivalent to a record in query2. The only reason for using Union over Concat is to remove duplicates and since you can't have any, you should likely be using Concat instead of Union.
public static void Dummy()
{
var query1 = this.Db.Table1.Select(s => new MyObject() { A = s.Field1, B = s.Field2 });
var query2 = this.Db.Table2.Select(s => new MyObject() { A = s.Field1, B = s.Field2, C = s.Field3 });
var result = query1.ToList().Concat(query2);
}
There are exceptions, as if you have a custom IEqualityComparer for MyObject that ignores the "C" property, or the default for the "C" property may exist in a record for table2, and you wanted to remove the duplicate, or if there possibly exists duplicates within either query1 or query2 and you wanted them removed then you can still use Concat, but you need to use Distinct before the Concat.
Editted to force query1 to be materialized before concatenation via .ToList()
Double checked with LinqPad, and the following executable had no issues, using a datasource that had both Categories and Cities tables of which were completely different schemas:
void Main()
{
var query1 = Categories.Select(s => new MyObject { A = s.id, B = s.name });
var query2 = Cities.Select(s => new MyObject { A = s.id, B = s.city_name, C = s.location });
var result = query1.ToList().Concat(query2);
result.Dump();
}
public class MyObject
{
public int A {get;set;}
public string B {get;set;}
public object C {get;set;}
}
I have an array for which I want to group the items based on a property. I tried the below code, but it is not grouping correctyly. MyArray is the array and Id is the property on which I want to do the grouping.
var docGroup = (from x in MyArray
group x by x.Id).Select(grp => new
{
Id = grp.Key,
Results = grp.ToList(),
})
.Results
.ToList());
To keep it simple if I just make it
var docGroup = from x in MyArray group x by x.Id;
where Id is a string "123" in the array and MyArray[2] has both the same Id. When I check the docGroup it has two entries and both have the 123 key instead of just one entry with the 123 key.
Here's a very simple example:
class Program
{
static void Main(string[] args)
{
Test[] tArray = new Test[3];
Test t = new Test() { Id = "123", Val="First" };
Test t1 = new Test() { Id = "123", Val="Second" };
Test t2 = new Test() { Id = "1234", Val="Third" };
tArray[0] = t;
tArray[1] = t1;
tArray[2] = t2;
var g = from x in tArray group x by x.Id;
}
}
class Test
{
public string Id { get; set; }
public string Val { get; set; }
}
Now if I look at g it has count 2 of which one is the Id 123 and the second is the Id 1234. I am not sure what is going wrong with my array. So this seems to work, but I am not sure what is going on with my array. I'll do some research on it.
Sorry guys, I found the issue. The Id was in a value property in MyArray which I was not using and so it was not grouping correctly. Thanks for the help everyone.
Everything works as expected.
GroupBy produces an enumerable of IGrouping. Since you have two distinct keys ("123" and "1234") you will get an enumerable of two elements. These grouping have a uniqe key and they're by themself enumerables.
So
g.Where(x => x.Key == "123").ToList();
will contain two elements (First, Second) and
g.Where(x => x.Key == "1233").ToList();
will contain one element (Third).
I have retrieved list of my specific class with 150 records.
Now, i want only those records which have Licenseid which are in my another int List.
For example My MainList
List<CustomerDocument> _list = GetListByID(CustomerID);
In this list i have column LicenseID,CustomerID,CustomerName,Type,Age e.t.c
And SecontList
List<int> type = new List<int>();
In Int list i add LicenseID one by one dynamically.
Public class CustomerDocument
{
public int LicenseID{get;set;};
public int CustomerID{get;set;};
public string CustomerName{get;set;};
public int Age{get;set;};
}
This is my CustomerDocument class for which i am getting list.
And now suppose, If Int list has three records , then i want those records from my Main List which have these three LicenseID in my Int List using Linq.
_list = ???
List<CustomerDocument> list = new List<CustomerDocument>();
List<Int> types = new List<Int>();
MapSearchSalesEntities datacontext = new MapSearchSalesEntities();
var collection = ddlLicenseType.CheckedItems;
if (collection.Count > 0)
{
foreach (var item in collection)
{
int value = Convert.ToInt32(item.Value);
types .Add(value);
}
}
var query = (from t1 in datacontext.Licenses
select new CustomerDocument
{
LicenseID = t1.LicenseID,
CustomerID = t1.CustomerID,
CustomerName= t1.CustomerName,
Age= t1.Age,
});
list = query.ToList(); ---gives 150 Records
if (types != null && types.Count > 0)
{
list = list.Where(c => types.Contains(c.LicenseID)).ToList(); --- Gives 0 Records
}
The most efficient approach is to use Enumerable.Join:
var documents = from doc in _list
join licenseID in type
on doc.LicenseID equals licenseID
select doc;
if you want to replace the list:
_list = documents.ToList();
You could also use Enumerable.Where + List.Contains which is not as efficient but shorter:
_list = _list.Where(d => type.Contains(d.LicenseID)).ToList();
Using the LinQ Where method, this is very easy:
_list = _list.Where(c => type.Contains(c.LicenseID)).ToList();
Here is a linq query
var result = (from cust in _list join id in type on cust.LicenseID equals id select cust).ToArray();
I have an Entity like this:
public class Category
{
public int classid {get;set;}
public int itemid {get;set;}
public string label {get;set;}
}
So a List produces this JSON (three sizes and three colors
[{"classid":1,"itemid":1,"label":"Small"},
{"classid":1,"itemid":2,"label":"Medium"},
{"classid":1,"itemid":3,"label":"Large"},
{"classid":2,"itemid":1,"label":"Blue"},
{"classid":2,"itemid":2,"label":"Green"},
{"classid":2,"itemid":3,"label":"Red"},
{"classid":3,"itemid":1,"label":"Tee"},
{"classid":3,"itemid":2,"label":"Golf"},
{"classid":3,"itemid":3,"label":"Dress"}]
However the JavaScript client needs something like this myarray[][].label:
[[{"itemid":1,"label":"Small"},
{"itemid":2,"label":"Medium"},
{"itemid":3,"label":"Large"}],
[{"itemid":1,"label":"Blue"},
{"itemid":2,"label":"Green"},
{"itemid":3,"label":"Red"}],
[{"itemid":1,"label":"Tee"},
{"itemid":2,"label":"Golf"},
{"itemid":3,"label":"Dress"}]]
And this is smack dab in the middle of my Linq query.
How would I construct the Linq query to assemble the two dimensional array from the one dimensional array within Linq?
EDIT: Existing Query:
...
CATS = (from myP in myProduct.ProductCategories
select new ProductCategory
{
classid = myP.classid,
itemid = myP.itemid,
label = myP.label
}),
...
EDIT: Getting Closer:
CATS = (from myP in myProduct.ProductCategories
group myP by myP.classid into groups
select new resultClass
{ classid = groups.Key,
opts = groups.Select(x =>
new ProductOption
{ itemid = x.itemid,
label = x.label}) }),
I haven't tested this, but it's familiar territory and should work:
IEnumerable<Category> items = ...;
var groups = items.GroupBy(x => x.classid);
var arrays = groups.Select(x =>
x.Select(y => new { itemid = y.itemid, label = y.label }).ToArray()
).ToArray();