Compare two IQueryable instances - c#

I have two IQueryable instances - objIQuerableA and objIQueryableB and I want to obtain only elements that are present in objIQuerableA and not in objIQuerableB.
One way is to use a foreach loop but I wonder if there is a better method.

Simple and straight forward.
var result = objIQuerableA.Except(objIQuerableB);

The title actually says compare two IQueryables. If you wanted to actually do a compare to determine if both IQueryable contain the same results in a single query....
var aExceptB = objIQuerableA.Except(objIQuerableB);
var bExceptA = objIQuerableB.Except(objIQuerableA);
var symmetricDiff = aExceptB.Union(bExceptA);
bool areDifferent = symmetricDiff.Any();

Related

Fastest way to match members of two lists

I have two lists which are orderHeaders and orderLines. These are two related tables in the database however when I pull them I have to pull them separately as two different lists and then map them out to each other later. I have a solution right now but the performance is a little bit disappointing given that I have around 400k headers and 1million+ lines.
Here's my code below. Is this the standard way to iterate over and find members inside two lists or is there a more optimized approach in C#?
var OutboundOrderHeaders =
DbContext.Context.Database.SqlQuery<OutboundOrderDTO>(queryString, parameter);
var OutboundOrderHeadersList = OutboundOrderHeaders.ToList();
var OutboundOrderLine =
DbContext.Context.Database.SqlQuery<OutboundOrderLineDTO>(queryStringLine, parameter2);
var OutboundOrderLineList = OutboundOrderLine.ToList();
for(var i = 0; i < OutboundOrderHeadersList.Count(); i++)
{
var LineToAdd = OutboundOrderLineList
.Where(x => x.OutboundNumber == OutboundOrderHeadersList[i].OutboundNumber)
.ToList() ;
OutboundOrderHeadersList[i].OrderLine = LineToAdd;
}
return OutboundOrderHeadersList;
As noted in comments, I'd really try hard to do this in the database rather than in memory. But to do it in memory, ToLookup is probably the right way to go:
// Note: here I've renamed used outboundOrderLines where you've got OutboundOrderLineList,
// and orderHeaders where you've got OutboundOrderHeadersList, as simpler
// and more conventional variable names.
var linesByOutboundNumber = outboundOrderLines.ToLookup(line => line.OutboundNumber);
foreach (var orderHeader in orderHeaders)
{
orderHeader.OrderLine = linesByOutboundNumer[orderHeader.OutboundNumber].ToList();
}
This builds a map going from outbound number to "all the lines with that outbound number" by going through outboundOrderLines once, rather than iterating over it for every order header.

Is this loop possible to do using simple LINQ?

I got a class called BG which has a property called Name Code.
I instantiate an object called bgList.
Now I am trying to get all the Code of the objects which have their 'Crop' property set to cropName.
I would like to convert the following working code to linq but for the life of me am unable to do that - am quite sure that I am missing something:
List<string> breedingGroupsAndRoles = new List<string>();
for (int i = 0; i < bgList.Count; i++)
{
if (bgList[i].Crop == cropName)
breedingGroupsAndRoles.Add(bgList.[i].Code);
}
The closest I came was this but it only nets me the first item:
breedingGroupsAndRoles.Add(bgrList.Find(c => c.Crop == cropName).Role);
List<string> breedingGroupsAndRoles = bgList
.Where(bg => bg.Crop == cropName)
.Select(bg => bg.Code)
.ToList();
Just for the sake of completeness, the Find method you tried calling on bgList is not part of LINQ, it's a member of the generic List class itself. It only returns the first element matched by the predicate you provide, which is why you were only getting one result. You probably wanted the FindAll method, which returns a list of all matching elements:
List<BG> breedingGroups = bgList.FindAll(c => c.Crop == cropName);
Note that this produces a list of matching BG instances rather than just their Role properties. Depending on how you're processing the results this may be sufficient, otherwise you'll still need LINQ or a loop and a second list to extract the Role values. In any case, an all-LINQ solution such #Tim Schmelter's is likely the better way to go.

Getting one list from another list using LINQ

I've seen the technique of taken a list of ObjectA and converting into a list of ObjectB where the two classes share some similar properties but is there an easier way to do that when you're just going from a list of ObjectA to another list of ObjectA?
Basically I want to do ...
var excv = from AuditedUser in data
where AuditedUser.IsMarkedForRemoval == false
select AuditedUser;
... but instead of a var I want the results to form a new List < AuditedUser > .
Is there something super easy I'm just missing?
var excv = (from AuditedUser in data
where AuditedUser.IsMarkedForRemoval == false
select AuditedUser).ToList();
I wrapped your LINQ statement with parens and added the ToList call at the end. Is this what you're looking for?
You could also do the following which is shorter to type and read I think:
List<AuditUser> excv = data.Where(a=>!a.IsMarkedForRemoval).ToList();

Better way of searching through lists than using foreach

list vclAsset<FullAsset>
list callsigns<string>
foreach(FullAsset fa in vclAsset)
{
if (callsigns.contains(fa.asset.callsign))
{
//do something
}
}
Is there a more elegant way to do the above? A FullAsset object contains an Asset object which in turn has a string "Callsign." Each callsign will be unique, so my list callsigns will only have one of each string, and no two FullAsset objects will share an Asset.callsign variable.
In a nutshell I want to pull all the FullAssets that have a certain callsign, but using a foreach seems clumsy (given that the number of FullAssets that could be contained in said list potentially has no upper limit).
You could use a lambda expression, something like this:
foreach(FullAsset fa in vclAsset.Where(a => callsigns.contains(a.asset.callsign))
{
// do something
}
If your keys are unique, you can use a Dictionary or a Hashtable to speed up searching.
If you only want to find a certain item, you can use the List<T>.Find method and supply a predicate.
FullAsset result = vclAsset.Find
(fa => callsigns.contains(fa.asset.callsign));
or
List<FullAsset> results = vclAsset.FindAll
(fa => callsigns.contains(fa.asset.callsign));
If you are using .Net 3.5, LINQ Where may be a better solution, as others have stated, since it returns an enumerator (lazy evaluation) vs a full List.
Sure, using linq.
var assets= vclAsset.Where(fullA=>allsigns.contains(fullA.asset.callsign));
assets will be some enumerable structure.
I can recommend 100 Linq samples for inspiration and learning
Not sure if it counts as more elegant but you can use linq...
var results = from fa in vclAsset
where callsigns.Contains(fa.asset.callsign)
select fa;
var result = vclAsset.Where(x=>callsigns.Any(y=>x.asset.callsign==y));
P.s. I would rename vclAsset and asset/callsign properties.
You can also use the Join function to do this.
var sortedList = vclAsset.Join(callsigns,
x => x.asset.callsign, x => x,
x, y => x);
This is the list of vclAssets that have the listed callsign.

is it possible to use Take in LINQ when the query returns an anonymous type?

I'm trying to get the n-th element out of a list of anonymous types returned by a LINQ query where n is a random number from 0 to 100. Messed around with it a while now and I'm not getting anywhere. My code (with names changed to protect IP):
var query = from Table1 t1 in theContext.Table1
join Table2 t2 in theContext.Table2
on ...
where ...
select new
{
partNum = t1.part_number,
partSource = t2.part_source
}
int num = new Random().Next(0, 100);
// here's where the code I've tried fails
Can I somehow do a Take<T>(100).ToList<T>()[num] to get a single anonymous type with partNum and partSource? I ended up solving this by explicitly defining a type, but it seemed like I was missing a more elegant solution here. All I want to do is return a Dictionary<string, string> to the caller so I'd prefer not to have to define a type outside of this method.
Update: ElementAt doesn't work for this. I tried adding:
// get a random part from the parts list
int num = new Random().Next(0, query.Count() - 1 );
var nthElement = query.ElementAt(num);
And I got an exception: The query operator 'ElementAt' is not supported.
You should be able to use:
var item = query.Take(100).ToList()[num];
Of course, it would be more efficient to do:
var item = query.Skip(num).First();
I believe you just want the ElementAt extension method:
var nthElement = query.ElementAt(num);
No need to mess with Take queries or such, and certainly not ToList.

Categories

Resources