it looks fairly simple but I can not get my mind over it.
I have a list:
List<IGrouping<byte, MyClass>>
MyClass object has a timestamp property, and I also have list of timestamp, now I want to know if there is elegant way to get all values from grouping list where Timestamp property is in my timestamp list?
I have solved the problem, but i think it can be solved in more efficient way. code would look like:
var loadedValues = new List<MyClass>();
foreach (IGrouping<byte, MyClass> value in Values)
{
loadedValues.AddRange(value.Select(c => c).Where(c => Timestamps.Any(point => c.Timestamp == point)));
}
You could use Join which uses a set based approach, hence it's more efficient than Where:
IEnumerable<MyClass> query =
from grp in Values
from cls in grp
join ts in Timestamps on cls.Timestamp equals ts
select cls;
List<MyClass> loadedValues = query.ToList();
Related
I have list of objects of a class for example:
class MyClass
{
string id,
string name,
string lastname
}
so for example: List<MyClass> myClassList;
and also I have list of string of some ids, so for example:
List<string> myIdList;
Now I am looking for a way to have a method that accept these two as paramets and returns me a List<MyClass> of the objects that their id is the same as what we have in myIdList.
NOTE: Always the bigger list is myClassList and always myIdList is a smaller subset of that.
How can we find this intersection?
So you're looking to find all the elements in myClassList where myIdList contains the ID? That suggests:
var query = myClassList.Where(c => myIdList.Contains(c.id));
Note that if you could use a HashSet<string> instead of a List<string>, each Contains test will potentially be more efficient - certainly if your list of IDs grows large. (If the list of IDs is tiny, there may well be very little difference at all.)
It's important to consider the difference between a join and the above approach in the face of duplicate elements in either myClassList or myIdList. A join will yield every matching pair - the above will yield either 0 or 1 element per item in myClassList.
Which of those you want is up to you.
EDIT: If you're talking to a database, it would be best if you didn't use a List<T> for the entities in the first place - unless you need them for something else, it would be much more sensible to do the query in the database than fetching all the data and then performing the query locally.
That isn't strictly an intersection (unless the ids are unique), but you can simply use Contains, i.e.
var sublist = myClassList.Where(x => myIdList.Contains(x.id));
You will, however, get significantly better performance if you create a HashSet<T> first:
var hash = new HashSet<string>(myIdList);
var sublist = myClassList.Where(x => hash.Contains(x.id));
You can use a join between the two lists:
return myClassList.Join(
myIdList,
item => item.Id,
id => id,
(item, id) => item)
.ToList();
It is kind of intersection between two list so read it like i want something from one list that is present in second list. Here ToList() part executing the query simultaneouly.
var lst = myClassList.Where(x => myIdList.Contains(x.id)).ToList();
you have to use below mentioned code
var samedata=myClassList.where(p=>p.myIdList.Any(q=>q==p.id))
myClassList.Where(x => myIdList.Contains(x.id));
Try
List<MyClass> GetMatchingObjects(List<MyClass> classList, List<string> idList)
{
return classList.Where(myClass => idList.Any(x => myClass.id == x)).ToList();
}
var q = myClassList.Where(x => myIdList.Contains(x.id));
i am having a issue while sorting data with more than a field
here is sample code i have used :
var Item = from itm in DB.Items
select new Item
{
};
return Item.ToList().OrderBy(e => e.ExpiryDate).ToList();
above code does'nt show any error but also doesn sort with expirydate field :(
if i use like this
return Item.OrderBy(e => e.ExpiryDate).ToList();
this gives a error that sql doesnt contain translation for orderby
any help
thanks in advance
I think there are a couple things going on here. First, you've named your result variable the same as the class you're creating in the select. Probably doesn't make much difference, but it's confusing. Let's change it:
var items = from itm in DB.Items
select new Item
{
};
Next, your DB.Items context is already built of Item objects. You don't need to create new ones. In the above code, your new Item objects are empty anyway.
var items = from itm in DB.Items
select itm;
If you want to order them right away, you can do that too:
var items = from itm in DB.Items
orderby itm.ExpiryDate
select itm;
If you need that as a List, you can do that in the same line:
var items = (from itm in DB.Items
orderby itm.ExpiryDate
select itm).ToList();
Now items will be a concrete List of Item objects (List<Item>).
Your code creates instances of Item using the default constructor, without passing anything that you get from the DB to it. That is why you get a list of identical empty items; sorting them has no effect.
Remove the first Select to make it work:
var Item = DB.Items; // No "Select ..."
return Item.ToList().OrderBy(e => e.ExpiryDate).ToList();
Your first ToList forces the data into memory, where OrderBy can be applied. You can replace it with an AsEnumerable() call as well, to avoid creating a list in memory for the second time:
return Item.AsEnumerable().OrderBy(e => e.ExpiryDate).ToList();
This is my first post, I hope the phrasing of my question makes sense, let me know if not.
I am struggling to write a linq query where the field I'm evaluating is nullable.
I have a collection of objects which has a nullable enum field and an id field (filteredCollection). I also have a collection of enums in a list (SearchCriterionQualificationUnitLevels).
I want to get a subset of the objects collection where the enum field matches one of the values in the list.
At the moment I have the following code which is working but I'd be interested in finding out how to achieve the same result without having to jump through so many hoops.
Dictionary<int, QualificationLevel> unitsWithLevels = filteredCollection.Where(x => x.QualificationLevel != null).ToDictionary(k => k.QualificationUnitID, v => v.QualificationLevel.Value);
var matchingUnits = from unitWithLevel in unitsWithLevels
join unitLevels in SearchCriterionQualificationUnitLevels on unitWithLevel.Value equals unitLevels
select unitWithLevel.Key;
filteredCollection = from unit in filteredCollection
join matchingUnit in matchingUnits on unit.QualificationUnitID equals matchingUnit
select unit;
Thanks in advance
This can be achieved in singe statement (new lines added for readability)
Dictionary<int, QualificationLevel> unitsWithLevels =
filteredCollection
.Where(x => x.QualificationLevel != null
&& SearchCriterionQualificationUnitLevels.Any(unitLevel => unitLevel.Equals(x.QualificationLevel.Value)));
var subset=filteredCollection.Where(i=>SearchCriterionQualificationUnitLevels.Any(l=>l==i.QualificationLevel));
I have a collection of dates (possibly duplicates) that I need to check against using .Contains() or a similar method.
I create an anonymous type like this:
var bookedDates = (from b in db.Bookings
where b.BookingDate > DateTime.Today
select new
{
Date = b.BookingDate,
StatusID = b.StatusId
});
I then have a collection of dates (d), and I need to test whether d exists within my anonymous type. This would be easy using a dictionary since I can use the .ContainsKey().
If a date exists within the anonymous type, I need to get the one or multiple items that correspond to the date I'm testing.
Is there a quick way to do this, I know I can do it by looping and testing each and every key but looking for a faster/more efficient way.
In essence, I'm looking for a dictionary that supports duplicate items.
If you just want to check whether you have the date already you can use a HashSet, then check the hashset in the following queries etc.:
HashSet<DateTime> dates = new HashSet<DateTime>();
foreach (var item in bookedDates)
dates.Add(item.Date);
..
if (dates.Contains(someDate))
{
//...
}
Edit:
I think you just want a lookup based on the items in your query:
var dateLookup = db.Bookings
.Where( b => b.BookingDate > DateTime.Today)
.ToLookup( p => p.BookingDate,
p => p.StatusId);
A lookup allows a collection of items for each key, so that might be what you are looking for.
Then you can just use it like this:
var statusIdsForToday = dateLookup[DateTime.Now.Date].ToList();
var distinctNames = bookedDates.Distinct();
Maybe I'm getting it wrong, but you can apply another LINQ query on your bookedDates, something like this:
DateTime searchDateTime = DateTime.Now; // or any other DateTime
var statusIds = (from b
in bookedDates
where b.Date == searchDateTime // or a similar comparison
select b.StatusID);
var statusIdsList = statusIds.ToList();
edit: if the search dates are from the same database then a join would be an option
var bookedIds = (from b
in db.Bookings
join dates in db.SearchDates on b.BookingDate equals dates
select b.StatusId);
(assuming that the dates to compare are located in db.SearchDates, if necessary, add a where dates > DateTime.Now or other restrictions on the dates)
var bookedDates = (from b in db.Bookings
where b.BookingDate > DateTime.Today
select new
{
date = b.BookingDate,
statusId = b.StatusId
}).GroupBy(x => x.date).Where(x => x.Count() > 1);
This should give you an IEnumerable of type IGrouping of type DateTime, StatusID type
I have IQueryable<someClass> baseList
and List<someOtherClass> someData
What I want to do is update attributes in some items in baseList.
For every item in someData, I want to find the corresponding item in baselist and update a property of the item.
someOtherClass.someCode == baseList.myCode
can I do some type of join with Linq and set baseList.someData += someOtherClass.DataIWantToConcantenate.
I could probably do this by iteration, but is there a fancy Linq way I can do this in just a couple lines of code?
Thanks for any tips,
~ck in San Diego
To pair elements in the two lists you can use a LINQ join:
var pairs = from d in someData
join b in baseList.AsEnumerable()
on d.someCode equals b.myCode
select new { b, d };
This will give you an enumeration of each item in someData paired with its counterpart in baseList. From there, you can concatenate in a loop:
foreach(var pair in pairs)
pair.b.SomeData += pair.d.DataIWantToConcantenate;
If you really meant set concatenation rather than +=, take a look at LINQ's Union, Intersect or Except methods.
LINQ is for querying - not for updating. That means it'll be fine to use LINQ to find the corresponding item, but for the modification you should be using iteration.
Admittedly you might want to perform some appropriate query to get baseList into an efficient form first - e.g. a Dictionary<string, SomeClass> based on the property you'll be using to find the corresponding item.
You can convert the IQueryable<SomeClass> into a List<SomeClass>, use the ForEach method to loop over it and update the elements, then convert back to IQueryable:
List<SomeClass> convertedList = baseList.ToList();
convertedList.ForEach(sc =>
{
SomeOtherClass oc = someData.First(obj => obj.SomeCode == sc.MyCode);
if (oc != null)
{
sc.SomeData += oc.DataIWantToConcatenate;
}
});
baseList = convertedList.AsQueryable(); // back to IQueryable
But it may be more efficient during this using non-LINQ constructs.
As mentioned before, it should be a combination of loop and LINQ
foreach (var someDataItem in someData)
{
someDataItem.PropertyToUpdate = (baseList.FirstOrDefault(baseListItem => baseListItem .key == someDataItem.key) ?? new SomeClass(){OtherProperty = "OptionalDefaultValue"}).OtherProperty;
}
You can't simply find objects that are in one list but not the other, because they are two different types. I'll assume you're comparing a property called OtherProperty that is common to the two different classes, and shares the same type. In that case, using nothing but Linq queries:
// update those items that match by creating a new item with an
// updated property
var updated =
from d in data
join b in baseList on d.OtherProperty equals b.OtherProperty
select new MyType()
{
PropertyToUpdate = d.PropertyToUpdate,
OtherProperty = d.OtherProperty
};
// and now add to that all the items in baseList that weren't found in data
var result =
(from b in baseList
where !updated.Select(x => x.OtherProperty).Contains(b.OtherProperty)
select b).Concat(updated);