I have two lists, one which is a list of Equipment and one which is a list of WorkflowItems with an Equipment property. The Equipment property within the List<WorkflowItem> list only has one of it's values hydrated, ProcessId. The List<Equipment> has two properties hydrated, ProcessId and Name. I want to hydrate the List<WorkflowItem>.Equipment.Name with the value from the single Equipment record in the List<Equipment>
This LINQ query below will select a generic item out doing basically what I'm looking for, but I would rather just fill in the original list.
var list = from item in workflowItems
join equipment in barcodeEquipmentList on
item.Equipment.ProcessId equals equipment.ProcessId
select new
{
ProcessId = item.Equipment.ProcessId,
EquipmentName = equipment.Name
};
Edit
The list is going to be relatively small, even doing something like this would be fine (aside from the fact that this does not work)
workflowItems.ForEach(x => x.Equipment = from e in barcodeEquipmentList
where e.Process.Id == x.Equipment.Process.Id
select e
);
...final edit
but this does work:
workflowItems.ForEach(x => x.Equipment = barcodeEquipmentList
.Where(e => e.Process.Id == x.Equipment.Process.Id)
.FirstOrDefault());
This piece of code should match your needs:
public class Equipment {
public int ProcessId { get; set; }
public string Name { get; set; }
}
public class WorkflowItem {
public Equipment { get; set; }
public void LoadEquipmentFrom(IEnumerable<Equipment> cache){
var equipment = cache.FirstOrDefault(e => e.ProcessId == Equipment.ProcessId);
if(equipment != null)
Equipment.Name = equipment.Name;
}
}
You could also assign the instance from cache to the existing one, it wouldn't matter since both must have the same Identifier Equipment = equipment;. That would be easier if you have more properties to set. To optimize further use an IDictionary<int, Equipment> instead of that IEnumerable<Equipment>, because you'll be reading that collection very often.
I'm guessing you are implementing a kind of ORM, in this case I can give you a good advice: "There is already something out there that'll fit your needs.".
Since the dataset was not very big (less than 20 records), I was able to this as below without a hit to performance
workflowItems.ForEach(x => x.Equipment = barcodeEquipmentList
.Where(e => e.Process.Id == x.Equipment.Process.Id)
.FirstOrDefault());
Related
I have 2 different classes:
public class ClassOne
{
public string ClassOneID { get; set; }
...
}
public class ClassTwo
{
public string ClassTwoID { get; set; }
...
}
I have IEnumerable instances of each. I want to return a List<ClassOne> that contains only the ClassOne items whose ClassOneID is equal to the ClassTwoID of a ClassTwo object from the second IEnumerable instance (if that makes sense!). I was thinking the following:
var list = new List<ClassOne>();
list.AddRange(classOneEnumerable.Where(o =>
classTwoEnumerable.Select(c => c.ClassTwoID == o.ClassOneID).First()));
This logic is contained within code that is some days off building/testing, so I am not actually able to run it just yet. I am not sure if what I have come up with is actually correct, and was hoping someone could put me right if I am mistaken.
var list = (from classOne in classOneEnumerable
from classTwo in classTwoEnumerable
where classOne.ClassOneID == classTwo.ClassTwoID
select classOne).ToList();
var list2 = (from classOne in classOneEnumerable
join classTwo in classTwoEnumerable
on classOne.ClassOneID equals classTwo.ClassTwoID
select classOne).ToList();
Both queries will yield the same results.
The existing answers are fine if you can handle O(n2). Otherwise I would sort the inner values so that you can get n log(n) performance.
Try this
var list = classOneEnumerable.Where(o => classTwoEnumerable
.Any(c => c.ClassTwoID == o.ClassOneID)))
.ToList();
I am busy implementing an api controller that returns a list of devices,
Here is my current helper method:
private List<Device> GetUsersDevices(string userName)
{
int userId = -1;
List<Device> myDevices;
if (Int32.TryParse(User.Identity.Name, out userId))
{
myDevices = db.Devices.Where((x => x.EndUserId == userId && x.Deleted == false)).OrderByDescending(x => x.LastComms).ToList();
return myDevices;
}
return null;
}
Now this is all good, But I now need to add a rule that it only returns certain devices (one compatible to the end client)
So I want to make a list of the allowed device types for a end client like the psuedo code below:
List endclient1 = device type 1, device type 2 etc.
List endclient2 = device type 2, device type 5 etc.
But then I want to filter my list of devices to remove all the unsupported devices. Now I understand that I can just continue adding to the .Where(...) clause in my original statement, but I want to know the most efficient way to go about doing this, And also If the amount of device types grows large it will not be very easy to maintain that line of code. And I would possibly like to have different list of supported devices for different end clients, So if I could filter by a list of allowed devices it will allow me to have one controller serving all the different end clients, and it will just return what device they are allowed.
Other Info that might be needed.
Here is the MyDevice Class
public class MyDevice
{
public MyDevice(long deviceId, string name, string serialNo, string deviceType, int deviceTypeId, bool enabled)
{
this.deviceId = deviceId;
this.name = name;
this.serialNo = serialNo;
this.deviceTypeName = deviceType;
this.deviceTypeId = deviceTypeId;
this.enabled = enabled;
}
public string name { get; set; }
public long deviceId { get; set; }
public string serialNo { get; set; }
public string deviceTypeName { get; set; }
public int deviceTypeId { get; set; }
public bool enabled { get; set; }
}
If I understand your question correctly, you want to do something like this:
var allowedDeviceTypes = GetAllowedDeviceTypesForUser(userId);
return db.Devices
.Where(x => x.EndUserId == userId && x.Deleted == false)
.Where(x => allowedDeviceTypes.Contains(x.deviceTypeId))
.OrderByDescending(x => x.LastComms)
.ToList();
This will be translated to a WHERE deviceTypeId IN (...) clause (assuming you're using LINQ to SQL, Entity Framework or NHibernate), which is efficient enough for a reasonable number of device types.
Note that the method GetAllowedDeviceTypesForUser has nothing to do with LINQ, and you're free to implement it however you want (e.g. delegate it to another service, add custom rules etc.). As long as you have a way of getting a list of allowed device types, you can pass that to the LINQ query.
Here's a trivial implementation:
private IDictionary<int, IList<int>> _allowedDeviceTypesPerUserId =
new Dictionary<int, IList<int>>() {
{ 1, new[] { 1, 2 } },
{ 2, new[] { 2, 5 } },
// ...
};
private IList<int> GetAllowedDeviceTypesForUser(int uesrId)
{
return _allowedDeviceTypesPerUserId[userId];
}
This is assuming the list of allowed devices per user is static. If it's dynamic, you'll have to load it from somewhere, in which case you might also consider using a Join but I'm not sure that's the best idea in your case.
You can take a look at join to give you an intersection of the "all devices" list and the "compatible devices" list, based on a key, in this case deviceId.
In this case, you would do something like (not checked for syntax correctness):
db.Devices.Join(endClientList, d => d.deviceId, ecd => ecd.deviceId, d => d);
This is just from the top of my head, so there may be some errors in there, but you should be able to figure out how to use it if you want.
Unless you have a few billion Devices I think you find plain old LINQ more than efficient:
myDevices = from device in db.Devices
where device.EndUserId == userId
&& device.Deleted == false
&& allowedTypes.Contains(device.Type)
order by device.LastComms
select device;
It is the most readable and maintainable too!
Another advantage of this is standard LINQ is compilers jitters conversions to SQL can all optimise this standard stuff - have you measured it's speed - is it a problem and have you compared it to alternatives?
I'm not sure I understand your question.But I will do just like this:
int userId = -1;
List<Device> myDevices;
if (Int32.TryParse(User.Identity.Name, out userId))
{
myDevices = db.Devices.Where((x => x.EndUserId == userId && x.Deleted == false)).OrderByDescending(x => x.LastComms).ToList();
var result1 = myDevices.Where(x=>x.name==someName);
var result2 = myDevices.Where(x=>x.deviceId >someDeviceId );
var result3 = myDevices.Where(x=>x.serialNo.Contains("someSerialNo"));
....
return resultN.ToList();
}
or
myDevices = from item in db.Devices
where item.name == someName && item.deviceId == someDeviceId && ...
where ...
OrderByDescending item.LastComms
select item;
Ok, so I have a List that contains a collection of message objects. An updated list of message objects comes in every 60 seconds. Some of the objects in the first collection will have updated data based on an ID property inside each object.
public class Message
{
public Int64 Id { get; set; }
public string Property1 { get; set; }
public DateTime MessageDate { get; set; }
}
How in LINQ can I Insert in the updated object based on the Id?
You need to get a reference to the object Message.
Something like this:
this.Messages.ForEach(mess => {
if(mess.Id == someValue){
// do something here
}
})
List<Message> LocalList;
List<Message> ArrivingList;
var mergedItems = LocalList.Contcat(ArrivingList);
mergedItems = (from msg in mergedItems
group msg by msg.Id into grp
let sameKey = mergedItems.Where(obj => obj.Id == grp.Id)
select sameKey.Where(obj => obj.MessageDate == grp.Max(obj2 => obj2.MessageDate)).Single()).ToList();
LocalList = (List<Message>)mergedItems;
I think something similar to the above would probably "work" but I would just use a standard Dictionary /List and write a small updating routine. Just because you CAN do something with a particular tool does not mean you SHOULD.
For me LINQ based stuff can be MUCH harder to troubleshoot, understand, debug, trace, evaluate, etc.
(Replace LocalList and ArrivingList in the above example with whatever your actual variable are)
I have an MVC controller that will filter a product list based on a category.
Products = repository.Products.Where(p => category == null || p.Category1 == "category1" );
If I wanted to let the user filter the product with two categories, I would have to add in another if statement that contains Category1 and Category2. I can imagine if I have more categories, and the user can choose category 1,3,5 and so on, the permutation will get crazily large.
Is there a proper way of doing this?
I am assuming that your object model is defined along the lines of:
public class Product
{
// ...
public Category Category1 { get; set; }
public Category Category2 { get; set; }
public Category Category3 { get; set; }
// ...
}
(where you might be using strings instead of having a category class)
If the object model is within your control, then I would recommend changing it so that a product has a collection of categories rather than several named properties for Category1, Category2, Category3 etc, so more like this:
public class Product
{
// ...
public IList<Category> Categories { get; set; }
// ...
}
If the product class is fixed and it already has multiple individual category properties, I would recommend writing an extension method for your product class that returns a list of categories that are non-null. That way you can write a where expression more succinctly.
For example:
public static class ProductExtension
{
public static IList<Category> GetCategories(this Product product)
{
List<Category> categories = new List<Category>();
if (product.Category1 != null)
{
categories.Add(product.Category1);
}
if (product.Category2 != null)
{
categories.Add(product.Category2);
}
// etc.
return categories;
}
}
...which could then be used along the lines of
repository.Products.Where(p => p.GetCategories().Contains("category1"));
Another option is to create a ProductFilter object to do the filtering for you.
Give the ProductFilter class a field for every category that is possible to filter on, which each store predicates, and a PassesFilter(Product p) method which determines whether p passes the predicate for all categories where a predicate has been set, e.g.
method PassesFilter(Product p):
if Category1Filter is not null:
if p does not pass Category1Filter:
return false
if Category2Filter is not null:
if p does not pass Category2Filter:
return false
return true
(Excuse the pseudo-code, I don't do C# and it's late)
So you could use it like so:
ProductFilter pf = new ProductFilter();
...
/*build up your filters for all categories that apply in this search...*/
pf.ColourFilter = (Product p) => { return p.Colour == "Red"; };
pf.PriceFilter = (Product p) => { return p.Price > 100.00; };
...
Products = repository.Products.Where(p => category == null || pf.PassesFilter(p) );
You could also easily implement the PassesFilter method differently to handle OR instead of AND (or create a class for each implementation).
I know that using predicates in the way I described would allow you to put a price predicate in the colour predicate field, but I just thought I'd throw this example out there to illustrate the concept of using an object to do the work of lambdas :-)
1.You may use Expression to constructor condition expression
2.Use expression in the linq.
How would one implement LINQ to extract the Guid's from one collection of objects of type A such that they can exclude these Guids from another collection of objects of type B. Object A and Object B both have a Guid field called 'ID."
I have the following:
ObservableCollection<Component> component Component has a
field called ID of type Guid
ObservableCollection<ComponentInformation> ComponentInformationCollection ComponentInformation
has a field called ID of type Guid
My implementation:
component =>
{
if (component != null)
{
var cancelledComponents = new List<ComponentInformation>();
foreach (Component comp in component)
{
cancelledComponents.Add(new ComponentInformation() { ID = comp.ID });
}
this.ComponentInformationCollection.Remove(cancelledComponents);
}
});
I believe there is a more elegant solution which I've been working at to solve but the issue I keep running into is creating a 'new ComponentInformation' such that the types do not give me an error.
====== FINAL SOLUTION =======
var cancelledComponentIDs = new HashSet<Guid>(component.Select(x => x.ID));
this.ComponentInformationCollection.Remove(
this.ComponentInformationCollection.Where(x => cancelledComponentIDs.Contains(x.ID)).ToList());
Thank you to:
Jason - I used this as a template for my final solution (listed below).
Servy - While I could have used a comparer, I think for this particular scenario a comparer was not neccessary because of its one-time-use type of situation.
ComponentInformationCollection is a Silverlight DependencyProperty that will trigger a INotifyChangedEvent (MVVM pattern) when altered, so the solution above worked best for my situation.
I would do this:
var ids = new HashSet<Guid>(
component.Select(x => x.ID)
);
var keepers = ComponentInformationCollection.Where(x => !ids.Contains(x.ID));
If Component doesn't already define an Equals and GetHashCode that uses the ID to do the compare you can define a comparer such as this:
class ComponentComparer : IEqualityComparer<Component>
{
public int Compare(Component a, Component b)
{
return a.ID.CompareTo(b.ID);
}
public int GetHashCode(Component a)
{
return a.ID.GetHashCode();
}
}
Then you can just use:
var result = componentCollectionA.Except(componentCollectionB, new ComponentComparer());
(written off of the top of my head; may require minor modifications to get it to compile.)
LINQ will allow you to find the GUIDs you need, but LINQ sequences are generally immutable; you'll still need to use some kind of loop to actually change the collection. The trick is getting the correct instances of your original collection that you want to remove.
Implementing one of the equality/comparison interfaces is one way to go, and if you need to compare your objects for equality in multiple places, is definitely the way to go. If you don't want to do that, this should get you what you want:
var removeme = (from x in this.ComponentInformationCollection
join y in component on x.ID equals y.ID
select x).ToList();
removeme.ForEach(x => this.ComponentInformationCollection.Remove(x));
Thinking out loud (meaning I didn't create a project and types and compile this), but how about:
var cancelledComponents = component.Select(c=> new ComponentInformation() {ID = c.ID}).ToList();
cancelledComponents.ForEach(c => ComponentInformationCollection.Remove(c));
There are a number of ways to solve this... this is a pretty simple Linq statement to query the ones you are looking for from the collection.
var keep = typeAList.Where(a => typeBList.FirstOrDefault(b => a.ID == b.ID) == null);
Here is the little test app I put together to demo it.
class Program
{
static void Main(string[] args)
{
List<TypeA> typeAList = new List<TypeA>();
typeAList.Add(new TypeA() { ID = Guid.NewGuid() });
typeAList.Add(new TypeA() { ID = Guid.NewGuid() });
typeAList.Add(new TypeA() { ID = Guid.NewGuid() });
List<TypeB> typeBList = new List<TypeB>();
typeBList.Add(new TypeB() { ID = typeAList[0].ID });
typeBList.Add(new TypeB() { ID = typeAList[1].ID });
//this is the statement
var keep = typeAList.Where(a => typeBList.FirstOrDefault(b => a.ID == b.ID) == null);
}
}
class TypeA
{
public Guid ID { get; set; }
}
class TypeB
{
public Guid ID { get; set; }
}