Get objects from collection with Linq - c#

I have an object in C# which has a few properties:
// Pseudo class
public class
{
Id;
To;
From;
}
I have alot instances of that class in a Collection. Which could look like this:
object 1:
Id: 1
To: "PathA"
From: "PathB"
object 2:
Id: 2
To: "PathB"
From: "PathC"
object 3:
Id: 3
To: "PathC"
From: "PathA"
Now what I want to do is get all the items from that collections where the value of To doesn't appear in the From in any of the objects. Which would result in the following:
object 1:
Id: 1
To: "PathA"
From: "PathB"
object 2:
Id: 2
To: "PathB"
From: "PathC"
Because the last object with Id: 3 has PathA in the From property, which already exists somewhere in the To property.
How can I do this with a Linq query?

Well, how to go about this problem? First, you could create an index of all the values of To. Then, filter your sequence based on the From property...
Something like:
var tos = new HashSet<string>(collection.Select(item => item.To));
var filtered = collection.Where(item => !tos.Contains(item.From));
You might want to check, if creating a HashSet really works like this, or if you need to construct it differently... but you get the idea. Sets are efficient, if the tos gets rather long, since you will be checking this a lot...

Say your collection of objects is as follows:
var objects = { object1, object2, object3 }
Then you want:
var result = objects.Where(o => !objects.Select(x => x.From).Contains(o.To));
If it concerns a large dataset, it might be smart to cache and store the sub-selection of "From" paths:
var fromPaths = new HashSet<string>(objects.Select(x => x.From));
var result = objects.Where(o => !fromPaths.Contains(o.To))

Firstly, your sample doesn't really match the question-text since all the sample objects have a To corresponding to some other From. But assuming the question-text is right and the sample is wrong:
How about with a group-join:
var query = from obj in collection
join fromObj in collection
on obj.To equals fromObj.From
into objGroup
where !objGroup.Any()
select obj;
Alternatively, build up a set of distinct Froms first:
var distinctFroms = new HashSet<string>(collection.Select(item => item.From));
var query = from obj in collection
where !distinctFroms.Contains(obj.To)
select obj;

var list = collection.Select(c=>c.To).Distinct().ToList();
var result = collection.Where(c=>!list.Contains(c.From)).ToList();

If you join the collection back onto itself using To and From as the respective keys, you can establish which items are "joined" via To/From and exclude them:
var itemsThatAreConnected =
collection.Join(collection, x => x.To, x => x.From, (a,b) => a);
var unconnected = collection.Except(itemsThatAreConnected);

Related

Mongo C# filter on integer contains

I've a collection with orders, each with an OrderId field of type int32. I would like to filter the collecting and get all orders where the OrderId contains e.g. 123. For instance the following OrderIds must match:
123
9912399
99123
The following raw mongo filter works:
{ $where: "/^.*123.*/.test(this.OrderId)" }
How can this be expressed in the C# Mongo client or something different that does the job?
I'm not able to use the below code, as it seems to only work in string fields:
Builders<Order>.Filter.Regex(x => x.OrderId, new BsonRegularExpression("123"))
ideally you should store a string version of the order id in your documents. in case that's not an option you can do the following aggregation pipeline:
var res = await collection
.Aggregate()
.AppendStage<object>("{ $set : { orderIdString : { $toString : '$OrderId' } } }")
.Match(Builders<object>.Filter.Regex("orderIdString", "/123/"))
.AppendStage<object>("{ $unset : 'orderIdString' }")
.As<Order>()
.ToListAsync();
I don't think you can generate $where via typed builders. So, as it was mentioned above, the only option you have is to create a filter from a raw MQL like below:
var coll = db.GetCollection<Order>("coll");
var result = coll.Find("{ $where: '/^.*123.*/.test(this.OrderId)' }").ToList();
Did you try using:
x => x.OrderId.ToString()
instead of
x => x.Orderid
You can use Filter.Regex to achieve the desired behavior:
var valueToFilterBy = "123"
var filter = Builders<Order>.Filter.Regex(nameof(Order.Id), new BsonRegularExpression($".*{valueToFilterBy}.*"));
var data = await collection.FindAsync(filter).ToListAsync();

Intersect two collections which contain different types

Suppose I have one collection, call it ids it is of type IEnumerable<string>, I have a second collection call it objects it's of type MyObject[]. MyObject has a string property called id. I would like a LINQ statement that returns all off the objects in the objects collection who's id matches any value in the ids collection. ids will be a strict subset of objects.Select(x => x.id). Meaning, for every string in ids I know there will be exactly one corresponding MyObject in objects. Can someone post a pure LINQ solution? I've tried a couple things with no luck. I can come up with an iterative solution easily enough so unless it's impossible to do with only LINQ please don't post any.
"Just" LINQ:
var r = obj.Where(o => ids.Any(id => id == o.id));
But better, for larger n, with a set:
var hs = new HashSet(ids);
var r = obj.Where(o => hs.Contains(o.id));
I think this is pretty straightforward with query syntax.
It would look something like:
var a = from o in objects
join i in ids on o.id equals i
select o;
If you just want a list of MyObject that match, you can do :
var solution = objects.Where(x=> ids.Contains(x.id));
With this instead, you'll get a List<T> where T is an Anonymous type with 2 properties, Id that is the string that work as "key" in this specific case, and Obj, a list of MyObject which id correspond to the Id property.
var solution = ids.Select(x=>new{ Id = x, Obj=objects.Where(y=>y.id == x).ToList()})
.ToList();
If you just want to know if there is any object in the intersection (which was what I was looking for)
Based on this
var a = from o in objects
join i in ids on o.id equals i
select o;
You can do this as well
var isEmpty = objects.Any(x => ids.Any(y => y == x.ToString()));
The accepted answer is correct. However, if someone doesn't like using SQL style LINQ, here is the LINQ extension method approach to solving the same problem.
var filteredObjects = objects.Join(ids, obj => obj.Id, id => id, (obj, _) => obj);
We are joining two different types, so the 2nd & 3rd Join parameter signify that join will be made on id.
The fourth parameter is used to select an object out of the resultant (obj, id) pair after applying join.

LINQ Where clause with Contains where the list has complex object

I've seen plenty of examples of LINQ with a contains on a simple list of objects:
var intList= new List<int>() { 1, 2, 3 };
var result = db.TableRecords.Where(c => intList.Contains(c.RecordId)).ToList();
What I'm trying to do seems slightly more complicated (I think). I have a line of code similar to this one gets me the list I need:
var xzList = db.Relations.Where(r => someOtherList.Contains(r.zId))
.Select(r => new { AId = r.xId, BId = r.zId })
.ToList();
And now I want to get the result similar to the previous example but the list now has an anonymous type in it with two ints. So how would I now get result where RecordId in TableRecords equals the AId in the anonymous type for each anonymous type in xzList?
Sounds like you are unsure how to get the values out of your anonymous type. You can use GunnerL3510's solution to dump it to a list, or you should be able to inline it like this:
var result =
db.TableRecords
.Where(c => xzList.Select(n => n.AId)
.Contains(c.RecordId))
.ToList();
Since you are naming the values in your anonymous type, you refer to them just like properties.
If you prefer to do a more structured approach, you can use this method.
Something like this:
db.TableRecords.Select(c=>c.RecordId).Intercept(xzList.Select(n => n.AId)).Any()

"new" inside concrete type projection is only called once

I've simple Linq2Sql query:
var result = from t in MyContext.MyItems
select new MyViewModelClass()
{
FirstProperty = t,
SecondProperty = new SomeLinq2SqlEntity()
}
The problem is that it seems that new SomeLinq2SqlEntity() is executed only once for the sequence, so all instances of MyViewModelClass in result of the query share the link to one object.
Update: Here is how I quickly check it:
result[0].SecondProperty.MyField = 10;
Using debugger I can check that MyField was set to 10 in all instances.
When I replace LINQ query with foreach, it works as expected:
var result = from t in MyContext.MyItems select t;
var list = new List<MyViewModelClass>();
foreach (var item in result)
{
list.add(new MyViewModelClass()
{
FirstProperty = item,
SecondProperty = new SomeLinq2SqlEntity()
});
}
I haven't found the root of the problem, but the post marked as asnwer provides good workaround. Check this asnwer for the detailed description: "new" inside concrete type projection is only called once
It probably has something to do with weird IQueryable implementation of your provider.
Aducci's answer extracts data from database with AsEnumerable() call and performs the query on that set, which is different from performing it through IQueryable.
For example IQueryable builds the ExpressionTree which it later parses according to the concrete provider (i.e. executing shared code once for optimization), while IEnumerable accepts Func and performs it as you would expect.
You can read more here:
http://msdn.microsoft.com/en-us/vcsharp/ff963710
Have you tried using adding the SomeLinq2SqlEntity object with linq to objects?
var result = (from t in MyContext.MyItems
select new
{
FirstProperty = t
})
.AsEnumerable()
.Select(t => new MyViewModelClass()
{
FirstProperty = t.FirstProperty ,
SecondProperty = new SomeLinq2SqlEntity();
});

LINQ to SQL find average of field?

I have a ViewModel called EntityRating, one of whose properties is AverageRating.
When I instantiate a new object of my ViewModel (called EntityRating) type, how do I set the EntityRating.AverageRating based on the Rating field (in SQL Server) of the item in question?
I want to do something like this (which obviously doesn't work):
var er = new EntityRating()
{
AverageRating = _db.All<Ratings>(X => X.RatingID = rating.RatingID).Average(RatingField);
};
Can I average the properties of an object in the database and assign it to the property of an object in my code?
(Pretty new, so let me know if any terminology is off, or if you need more info)
Thanks.
LINQ has the .Average extension method, however, that only works on integers. So what you need to do is get an IEnumerable of the RatingField property on all your Rating objects in the database. This can be accomplished by using the .Select extension method of LINQ which Projects each element of a sequence into a new form.
int average = _db.Ratings
.Where(x => x.RatingID == rating.RatingID)
.Select(x => x.RatingField)
.Average();
There's a LINQ function Average() seen here:
http://msdn.microsoft.com/en-us/library/bb399409.aspx
var er = new EntityRating()
{
AverageRating = _db.Where(X => X.RatingID == rating.RatingID)
.Select( x => x.RatingField).Average();
};

Categories

Resources