I am facing a scenario where I have to filter a single object based on many objects.
For sake of example, I have a Grocery object which comprises of both Fruit and Vegetable properties. Then I have the individual Fruit and Vegetable objects.
My objective is this:
var groceryList = from grocery in Grocery.ToList()
from fruit in Fruit.ToList()
from veggie in Vegetable.ToList()
where (grocery.fruitId = fruit.fruitId)
where (grocery.vegId = veggie.vegId)
select (grocery);
The problem I am facing is when Fruit and Vegetable objects are empty.
By empty, I mean their list count is 0 and I want to apply the filter only if the filter list is populated.
I am also NOT able to use something like since objects are null:
var groceryList = from grocery in Grocery.ToList()
from fruit in Fruit.ToList()
from veggie in Vegetable.ToList()
where (grocery.fruitId = fruit.fruitId || fruit.fruitId == String.Empty)
where (grocery.vegId = veggie.vegId || veggie.vegId == String.Empty)
select (grocery);
So, I intend to check for Fruit and Vegetable list count...and filter them as separate expressions on successively filtered Grocery objects.
But is there a way to still get the list in case of null objects in a single query expression?
I think the LINQ GroupJoin operator will help you here. It's similar to the TSQL LEFT OUTER JOIN
IEnumerable<Grocery> query = Grocery
if (Fruit != null)
{
query = query.Where(grocery =>
Fruit.Any(fruit => fruit.FruitId == grocery.FruitId));
}
if (Vegetable != null)
{
query = query.Where(grocery =>
Vegetable.Any(veggie => veggie.VegetableId == grocery.VegetableId));
}
List<Grocery> results = query.ToList();
Try something like the following:
var joined = grocery.Join(fruit, g => g.fruitId,
f => f.fruitId,
(g, f) => new Grocery() { /*set grocery properties*/ }).
Join(veggie, g => g.vegId,
v => v.vegId,
(g, v) => new Grocery() { /*set grocery properties*/ });
Where I have said set grocery properties you can set the properties of the grocery object from the g, f, v variables of the selector. Of interest will obviouly be setting g.fruitId = f.fruitId and g.vegeId = v.vegeId.
var groceryList =
from grocery in Grocery.ToList()
join fruit in Fruit.ToList()
on grocery.fruidId equals fruit.fruitId
into groceryFruits
join veggie in Vegetable.ToList()
on grocery.vegId equals veggie.vegId
into groceryVeggies
where ... // filter as needed
select new
{
Grocery = grocery,
GroceryFruits = groceryFruits,
GroceryVeggies = groceryVeggies
};
You have to use leftouter join (like TSQL) for this. below the query for the trick
private void test()
{
var grocery = new List<groceryy>() { new groceryy { fruitId = 1, vegid = 1, name = "s" }, new groceryy { fruitId = 2, vegid = 2, name = "a" }, new groceryy { fruitId = 3, vegid = 3, name = "h" } };
var fruit = new List<fruitt>() { new fruitt { fruitId = 1, fname = "s" }, new fruitt { fruitId = 2, fname = "a" } };
var veggie = new List<veggiee>() { new veggiee { vegid = 1, vname = "s" }, new veggiee { vegid = 2, vname = "a" } };
//var fruit= new List<fruitt>();
//var veggie = new List<veggiee>();
var result = from g in grocery
join f in fruit on g.fruitId equals f.fruitId into tempFruit
join v in veggie on g.vegid equals v.vegid into tempVegg
from joinedFruit in tempFruit.DefaultIfEmpty()
from joinedVegg in tempVegg.DefaultIfEmpty()
select new { g.fruitId, g.vegid, fname = ((joinedFruit == null) ? string.Empty : joinedFruit.fname), vname = ((joinedVegg == null) ? string.Empty : joinedVegg.vname) };
foreach (var outt in result)
Console.WriteLine(outt.fruitId + " " + outt.vegid + " " + outt.fname + " " + outt.vname);
}
public class groceryy
{
public int fruitId;
public int vegid;
public string name;
}
public class fruitt
{
public int fruitId;
public string fname;
}
public class veggiee
{
public int vegid;
public string vname;
}
EDIT:
this is the sample result
1 1 s s
2 2 a a
3 3
Related
If I have the following LINQ query:
var outstandingDataTotalData = from t1 in dtTotal.AsEnumerable()
join t2 in dtOutstandingData.AsEnumerable() on
new
{
priv_code = t1["priv_code"],
pri_ded = t1["pri_ded"].ToString().Trim()
} equals
new
{
priv_code = t2["priv_code"],
pri_ded = t2["pri_ded"].ToString().Trim()
}
into ps
from t2 in ps.DefaultIfEmpty()
select new
{
adjustment_value = t2 == null ? string.Empty : t2["adjustment_value"].ToString(),
amount_outstanding = t2 == null ? string.Empty : t2["amount_outstanding"].ToString(),
amount_outstanding_priv = t2 == null ? string.Empty : t2["amount_outstanding_priv"].ToString(),
amount_outstanding_ded = t2 == null ? string.Empty : t2["amount_outstanding_ded"].ToString(),
diff_outstanding = t2 == null ? string.Empty : t2["diff_outstanding"].ToString(),
exchange_rate = t2 == null ? string.Empty : t2["exchange_rate"].ToString(),
SalYear = t2 == null ? string.Empty : t2["sal_year"].ToString(),
SalMonth = t2 == null ? string.Empty : t2["sal_mon"].ToString()
};
Now outstandingDataTotalData is a list of anonymous type. And I have the following class:
public class AdjustmentTotal
{
public string SalYear { get; set; }
public string SalMonth { get; set; }
public string Value { get; set; }
}
How to loop through outstandingDataTotalData properties to fill List<AdjustmentTotal> as the following example:
If the result set of outstandingDataTotalData =
[0]{ adjustment_value = "100.00", amount_outstanding = "80.00", amount_outstanding_priv = "60.00", amount_outstanding_ded = "30.52", diff_outstanding = "0.36", exchange_rate = "", SalYear = "2018", SalMonth = "1" }
[1]{ adjustment_value = "1500.00", amount_outstanding = "5040.00", amount_outstanding_priv = "", amount_outstanding_ded = "", diff_outstanding = "0.36", exchange_rate = "", SalYear = "2018", SalMonth = "1" }
I want the result set of List<AdjustmentTotal> as:
2018 1 100.00
2018 1 1500.00
2018 1 80.00
2018 1 5040.00
2018 1 60.00
2018 1
2018 1 30.52
2018 1
2018 1 0.36
2018 1 0.36
2018 1
2018 1
Make your life easy, don't extract to separate properties. Make the extract as an array:
select new {
someArray = new[]{
t2["adjustment_value"].ToString(),
t2["amount_outstanding"].ToString(),
t2["amount_outstanding_priv"].ToString(),
t2["amount_outstanding_ded"].ToString(),
...
},
SalYear = ...,
}
So you end up with an object with 3 properties, two strings SalXxx and an array of strings (the other values). The array of strings means you can use the LINQ SelectMany it to flatten it. You'll see in the msdn example they have owners with lists of pets (variable number of pets but your values are fixed number) and after selectmany it's flattened to a list where the owner repeats and there is one pet. Your lowercases values are the pets, the SalXxx values are the owners
Once you get a working query you can actually integrate it into the first query ..
Sorry for not posting a full example (and I skipped the null checks for clarity) - the code is very hard to work with on a cellphone
Edit:
So, you say you want the results in a particular order. Both Select and SelectMany have a version where they will give the index of the item, and we can use that.. because you basically want to have these objects:
var obj = new [] {
new { SalYear = 2018, SalMonth = 1, C = new[] { "av1", "ao1", "aop1", "aod1" } },
new { SalYear = 2018, SalMonth = 2, C = new[] { "av2", "ao2", "aop2", "aod2" } }
};
Be like av1, av2, ao1, ao2.. so we want to sort the results first by the index of the inner array, then by the index of the outer array
We use a SelectMany to dig out the inner array and then for every item in the inner array we make a new object that has the data and the indexes (of the inner and the outer):
obj.SelectMany((theOuter, outerIdx) =>
theOuter.C.Select((theInner, innerIdx) =>
new {
SalYear = theOuter.SalYear,
SalMonth = theOuter.SalMonth,
DataItem = theInner,
OuterIdx = outerIdx,
InnerIdx = innerIdx
}
)
).OrderBy(newObj => newObj.InnerIdx).ThenBy(newObj => newObj.OuterIdx)
You will probably find you don't need the ThenBy; sorting by the InnerIdx will leave a tie (every InnerIdx in my list is represented twice -there are two innerIdx=0 etc) and things in linq sort as far as they can then no futher - because theyre sorted by OuterIdx already (when they went into the query) they should remain sorted by OuterIdx after they tie on InnerIdx.. If that makes sense. Belt and braces!
outstandingDataTotalData.Select(s => new AdjustmentTotal {
SalYear = s.SalYear,
SalMonth = s.SalMonth,
Value = s.adjustment_value
}).ToList();
Use a eum :
class Program
{
static void Main(string[] args)
{
List<AdjustmentTotal> totals = new List<AdjustmentTotal>();
for (int i = 0; i < (int)VALUE.END; i++)
{
foreach (var data in outstandingDataTotalData)
{
AdjustmentTotal total = new AdjustmentTotal();
totals.Add(total);
total.SalMonth = data.SalMonth;
total.SalYear = data.SalYear;
total._Type = (VALUE)i;
switch ((VALUE)i)
{
case VALUE.adjustment_value :
total.Value = data.adjustment_value;
break;
case VALUE.amount_outstanding:
total.Value = data.amount_outstanding;
break;
case VALUE.amount_outstanding_ded:
total.Value = data.mount_outstanding_ded;
break;
case VALUE.amount_outstanding_priv:
total.Value = data.amount_outstanding_priv;
break;
case VALUE.diff_outstanding:
total.Value = data.diff_outstanding;
break;
case VALUE.exchange_rate:
total.Value = data.exchange_rate;
break;
}
}
}
}
}
public enum VALUE
{
adjustment_value = 0,
amount_outstanding = 1,
amount_outstanding_priv = 2,
amount_outstanding_ded = 3,
diff_outstanding = 4,
exchange_rate = 5,
END = 6
}
public class AdjustmentTotal
{
public string SalYear { get; set; }
public string SalMonth { get; set; }
public string Value { get; set; }
public VALUE _Type { get; set; }
}
I've got two separate lists of custom objects. In these two separate lists, there may be some objects that are identical between the two lists, with the exception of one field ("id"). I'd like to know a smart way to query these two lists to find this overlap. I've attached some code to help clarify. Any suggestions would be appreciated.
namespace ConsoleApplication1
{
class userObj
{
public int id;
public DateTime BirthDate;
public string FirstName;
public string LastName;
}
class Program
{
static void Main(string[] args)
{
List<userObj> list1 = new List<userObj>();
list1.Add(new userObj()
{
BirthDate=DateTime.Parse("1/1/2000"),
FirstName="John",
LastName="Smith",
id=0
});
list1.Add(new userObj()
{
BirthDate = DateTime.Parse("2/2/2000"),
FirstName = "Jane",
LastName = "Doe",
id = 1
});
list1.Add(new userObj()
{
BirthDate = DateTime.Parse("3/3/2000"),
FirstName = "Sam",
LastName = "Smith",
id = 2
});
List<userObj> list2 = new List<userObj>();
list2.Add(new userObj()
{
BirthDate = DateTime.Parse("1/1/2000"),
FirstName = "John",
LastName = "Smith",
id = 3
});
list2.Add(new userObj()
{
BirthDate = DateTime.Parse("2/2/2000"),
FirstName = "Jane",
LastName = "Doe",
id = 4
});
List<int> similarObjectsFromTwoLists = null;
//Would like this equal to the overlap. It could be the IDs on either side that have a "buddy" on the other side: (3,4) or (0,1) in the above case.
}
}
}
I don't know why you want a List<int>, i assume this is what you want:
var intersectingUser = from l1 in list1
join l2 in list2
on new { l1.FirstName, l1.LastName, l1.BirthDate }
equals new { l2.FirstName, l2.LastName, l2.BirthDate }
select new { ID1 = l1.id, ID2 = l2.id };
foreach (var bothIDs in intersectingUser)
{
Console.WriteLine("ID in List1: {0} ID in List2: {1}",
bothIDs.ID1, bothIDs.ID2);
}
Output:
ID in List1: 0 ID in List2: 3
ID in List1: 1 ID in List2: 4
You can implement your own IEqualityComparer<T> for your userObj class and use that to run a comparison between the two lists. This will be the most performant approach.
public class NameAndBirthdayComparer : IEqualityComparer<userObj>
{
public bool Equals(userObj x, userObj y)
{
return x.FirstName == y.FirstName && x.LastName == y.LastName && x.BirthDate == y.BirthDate;
}
public int GetHashCode(userObj obj)
{
unchecked
{
var hash = (int)2166136261;
hash = hash * 16777619 ^ obj.FirstName.GetHashCode();
hash = hash * 16777619 ^ obj.LastName.GetHashCode();
hash = hash * 16777619 ^ obj.BirthDate.GetHashCode();
return hash;
}
}
}
You can use this comparer like this:
list1.Intersect(list2, new NameAndBirthdayComparer()).Select(obj => obj.id).ToList();
You could simply join the lists on those 3 properties:
var result = from l1 in list1
join l2 in list2
on new {l1.BirthDate, l1.FirstName, l1.LastName}
equals new {l2.BirthDate, l2.FirstName, l2.LastName}
select new
{
fname = l1.FirstName,
name = l1.LastName,
bday = l1.BirthDate
};
Instead of doing a simple join on just one property (column), two anonymous objects are created new { prop1, prop2, ..., propN}, on which the join is executed.
In your case we are taking all properties, except the Id, which you want to be ignored and voila:
Output:
And Tim beat me to it by a minute
var similarObjectsFromTwoLists = list1.Where(x =>
list2.Exists(y => y.BirthDate == x.BirthDate && y.FirstName == x.FirstName && y.LastName == x.LastName)
).ToList();
This is shorter, but for large list is more efficient "Intersect" or "Join":
var similarObjectsFromTwoLists =
list1.Join(list2, x => x.GetHashCode(), y => y.GetHashCode(), (x, y) => x).ToList();
(suposing GetHashCode() is defined for userObj)
var query = list1.Join (list2,
obj => new {FirstName=obj.FirstName,LastName=obj.LastName, BirthDate=obj.BirthDate},
innObj => new {FirstName=innObj.FirstName, LastName=innObj.LastName, BirthDate=innObj.BirthDate},
(obj, userObj) => (new {List1Id = obj.id, List2Id = userObj.id}));
foreach (var item in query)
{
Console.WriteLine(item.List1Id + " " + item.List2Id);
}
I have a list of "Couple" with the following structure
public class Couple
{
public string Code;
public string Label;
}
And another list of "Stuff" like that
public class Stuff
{
public string CodeStuff;
public string LabelStuff;
public int foo;
public int bar;
//etc...
}
I want to get all the Stuff objects where each couples (Code, Label) in my List<Couple> match with (CodeStuff, Label,Stuff) from List<Stuff>.
For example the Couple
Couple c = new Couple { Code = "ABC", Label = "MyLabel1" };
will match with the first row only from
Stuff s1 = new { CodeStuff = "ABC", LabelStuff = "MyLabel1" }; // OK
Stuff s1 = new { CodeStuff = "ABC", LabelStuff = "MyLabel2" }; // NOT OK
Stuff s1 = new { CodeStuff = "DEF", LabelStuff = "MyLabel1" }; // NOT OK
I tried to use .Where clause or .Foreach but I don't know how to get the couple (Code,Label) together. Can you help me ?
You need to use linq join.
List<Couple> lstcouple = new List<Couple>();
List<Stuff> lstStuff = new List<Stuff>();
var result = (from s in lstStuff
join c in lstcouple on new { CS = s.CodeStuff, LS = s.LabelStuff } equals new { CS = c.Code, LS = c.Label }
select s).ToList();
Have you tried a join? might need tweaking, but as an idea:
var joined = from c in couplesList
join s in stuffList on c.Code equals s.CodeStuff
where c.Label == s.LabelStuff
select s;
Try this:
List<Stuff> stuffs = new List<Stuff>{s1,s2,s3};
List<Couple> couples = new List<Couple>{c};
var filteredList = stuffs.
Where
(x=> couples.Any(y => y.Code == x.CodeStuff)
&& couples.Any(y=> y.Label == x.LabelStuff)
).ToList();
var pairs = new [] { new { id = 1, name = "ram", dept = "IT", sal = "3000" }, new { id = 2, name = "ramesh", dept = "IT", sal = "5000" }, new { id = 3, name = "rahesh", dept = "NONIT", sal = "2000" },
new { id = 5, name = "rash", dept = "NONIT", sal = "7000" } };
var query = from stud in pairs
where (stud.name.StartsWith("r") && stud.id % 2 != 0)
//orderby stud.sal descending
group stud by stud.dept into grps
select new { Values = grps, Key = grps.Key, maxsal=grps.Max() };
////select new { id = stud.id };
foreach (dynamic result in query)
{
Console.WriteLine(result.Key);
Console.WriteLine(result.maxsal);
foreach (dynamic result2 in result.Values)
{
Console.WriteLine(result2.id + "," + result2.sal);
}
}
Console.Read();
I am getting the error "At least one object must implement IComparable.", can someone explain me why iam I getting this error ?
You are calling grps.Max() to get maximnum item in group. Your anonymous objects are not comparable. How Linq will know which one is maximum from them? Should it use id property for comparison, or name?
I believe you want to select max salary:
maxsal = grps.Max(s => Int32.Parse(s.sal))
I am trying to reproduce the following SQL query in Linq and need some help please.
select
dbo.Documents.DocId,
dbo.Documents.ReykerAccountRef,
dbo.Documents.ReykerClientId DocClientID,
CAAs.ClientId CAAClientIDCheck,
ClientData.FullName ClientFullName,
CAAs.IFAId,
AdvisorData.FullName AdvisorFullName
from dbo.Documents
left join
dbo.CAAs on dbo.Documents.ReykerAccountRef = dbo.CAAs.AccountRef
left join
dbo.hmsProfileDatas AS ClientData
on
dbo.CAAs.ClientId = ClientData.ReykerClientID
left join
dbo.hmsProfileDatas AS AdvisorData
on
dbo.CAAs.IFAId = AdvisorData.ReykerClientID
I am trying to link to the same table twice, once for a client fullname and the other for an Advisor fullname.
The basic sql I want to produce in linq is
select table1.*,table2.*,a.Fullname, b.Fullname
from table1
left join
table2 on table1.t2Id = table2.Id
left join
table3 AS a
on
table2.t3Id1 = table3.id1
left join
table3 AS b
on
table2.t3Id2 = table3.id2
So table one is joined to table2 and table2 has 2 foreign keys (t3Id1 and t3Id2) to table3 to different fields (id1 and id2).
This is what I have tried following some guidance, but it's not returning anything! What's going wrong?
var results3 = from doc in DataContext.Documents
from caa
in DataContext.CAAs
.Where(c => c.AccountRef == doc.ReykerAccountRef)
.DefaultIfEmpty()
from cpd
in DataContext.hmsProfileDatas
.Where(pdc => pdc.ReykerClientID == caa.ClientId)
.DefaultIfEmpty()
from apd
in DataContext.hmsProfileDatas
.Where(pda => pda.ReykerClientID == caa.IFAId)
.DefaultIfEmpty()
select new DocumentInList()
{
DocId = doc.DocId,
DocTitle = doc.DocTitle,
ReykerDocumentRef = doc.ReykerDocumentRef,
ReykerAccountRef = doc.ReykerAccountRef,
ClientFullName = cpd.FullName,
AdvisorFullName = apd.FullName,
DocTypeId = doc.DocTypeId,
DocTypes = doc.DocTypes,
DocDate = doc.DocDate,
BlobDocName = doc.BlobDocName,
UploadDate = doc.UploadDate,
};
I hope I understood your example correctly. Here's another simple example, that should give what you need:
private class User
{
public int UserId;
public string Name;
public int GroupId;
public int CollectionId;
}
public class Group
{
public int GroupId;
public string Name;
}
public class Collection
{
public int CollectionId;
public string Name;
}
static void Main()
{
var groups = new[] {
new Group { GroupId = 1, Name = "Members" },
new Group { GroupId = 2, Name = "Administrators" }
};
var collections = new[] {
new Collection { CollectionId = 1, Name = "Teenagers" },
new Collection { CollectionId = 2, Name = "Seniors" }
};
var users = new[] {
new User { UserId = 1, Name = "Ivan", GroupId = 1, CollectionId = 1 },
new User { UserId = 2, Name = "Peter", GroupId = 1, CollectionId = 2 },
new User { UserId = 3, Name = "Stan", GroupId = 2, CollectionId = 1 },
new User { UserId = 4, Name = "Dan", GroupId = 2, CollectionId = 2 },
new User { UserId = 5, Name = "Vlad", GroupId = 5, CollectionId = 2 },
new User { UserId = 6, Name = "Greg", GroupId = 2, CollectionId = 4 },
new User { UserId = 6, Name = "Arni", GroupId = 3, CollectionId = 3 },
};
var results = from u in users
join g in groups on u.GroupId equals g.GroupId into ug
from g in ug.DefaultIfEmpty()
join c in collections on u.CollectionId equals c.CollectionId into uc
from c in uc.DefaultIfEmpty()
select new {
UserName = u.Name,
GroupName = g != null ? g.Name : "<No group>",
CollectionName = c != null ? c.Name : "<No collection>"
};
}
It produces two join over one table to get data from other two tables.
Here's the output:
Ivan Members Teenagers
Peter Members Seniors
Stan Administrators Teenagers
Dan Administrators Seniors
Vlad <No group> Seniors
Greg Administrators <No collection>
Arni <No group> <No collection>