Access field from generic List in GetRange() method? - c#

Does anybody know how to access a specific field from List<>? I cant figure out howto access a specific field in newList object.
List<Liner> LX = new List<Liner>();
public class Liner
{
public double Temperature { get; set; }
public double Moisture { get; set; }
}
newList = LX.OrderBy(x => x.Temperature).ToList();
var lstMXLast = newList.GetRange(8755, 5); // I need only 5 specific Moisture records in this case.

GetRange returns a copy of the list with the given range. So your list needs at least 8760 items. To select only the Moisture property of your objects, you can use LINQ's Select:
var lstMoistures = newList.GetRange(8755, 5).Select(l => l.Moisture).ToList();
Note: you need the ToList at the end only if you want to persist the query. Your ToList at the end of the OrderBy query is useless because you want to chain another query. I would materialze LINQ queries only as late as possible.
You could also use LINQ for the whole thing:
var lstMoistures = newList.Skip(8755).Take(5).Select(l => l.Moisture).ToList();
Assuming that you originally wanted to select the 5 liners with the highest temperature, this should give you the correct result:
var lstMoistures = LX.OrderByDescending(x => x.Temperature).Take(5).Select(l => l.Moisture).ToList();

You can use newList.GetRange(8755, 5).Select(l => l.Moisture) to just get the Moisture component from the five selected Liner records.

Use
var results = newList.GetRange(8755, 5).Select(m => m.Moisture);
It will give you moisture levels of Liner returned by GetRange() i.e. 5 moisture levels.

Related

How to query a list and return values where ID = any value in an array

I have 2 collections. 1 is an array of groupIds, and the other is an IEnumerable.
My user class contains another list of type IList, which denotes the groups the user belongs to:
public class User
{
public IList<Group> Groups { get; protected set; }
}
And the Group class looks something like this:
public class Group{
public long Group_Id { get; protected set; }
}
What I need to do is return a collection of all users who are in the groupIds array. I.e., I would want something like:
usersRepository.Where(user => user.Groups.Where(group => groupIds.Contains(group.id))
Such that if my groupIds array has group id of 5 and 6, all users in the useresRepository which belong to group 5 and 6 are returned.
So far, the best I've been able to come up with is the following:
UserRepo.Users.AsEnumerable().Where(user =>
user.GroupUsers.Where(wg => GroupIds.Contains(wg.Group_Id)));
But this is complaining since .Contains returns a boolean instead of my list, but I'm not sure how to fix that.
My code doesn't work due to .Contains() causing the expression to return a boolean rather than a collection. However, I'm not sure how else to check of the list of GroupIds nested within my User object contains the specified group ids.
Try Any .This will give you all the users that have any of the group mentioned in the groupIds array
UserRepo.Users.AsEnumerable().Where(user =>
user.Groups.Any(wg => GroupIds.Contains(wg.Group_Id)));
So you have a sequence of Users, where every User has a sequence of Groups, where every Group has a GroupId.
You also have a sequence of GroupIds.
IEnumerable <int> groupIds =
IQueryable<User> users = ...
You want all Users, that have at least one Group with a GroupId that is in you sequence of GroupIds.
Whenever you see "I want all items that have at least one ...", consider using Any:
var result = users
.Where(user => user.groups
.Select(group => group.groupId)
.Any(groupId => groupIds.Contains(groupId));
In words: from every user, take its collection of Groups. From every Group take the groupIds, and check if any of these groupIds is in collection groupIds. If so, keep this user in your result.
This can be a little bit shorter:
var result = users.Where(user => user.groups.Any(group => groupIds.Contains(group.groupId));

What is the best way to optimize a nested where clause that has multiple conditions?

I'm trying to find a list of ReturnItems where the quantity of a single item being returned is more than the original ordered quantity for that item. So there are 2 different lists of objects in play here - IEnumerable<ReturnItem> and IEnumerable<OrderItem>. The problem is that depending on the source for making the return (there are multiple places in our workflow where a return can be made) the ItemNumber on a given ReturnItem may be null. In this case we would need to rely on the ReturnItem.OrderItemId to match it to an OrderItem.
I have solved the problem using LINQ, but it requires a nested for loop (under the hood) so I'm trying to avoid that if possible while also maintain readability.In other words, I want to avoid a run time of O(N^2) and look for O(N) or better but again, while maintaining readability (I know I'm asking for a lot here but I figured I'd see if anyone has a creative solution). I created a solution where I have two dictionaries for the order items. One of them, the key is the item number and the other's key is the order Item Id. This works and solves the performance problem, but I completely loose the readability.
Here is the original LINQ statement I had:
// ItemsForReturn = IEnumerable<ReturnItem>
// OrderItems = IEnumerable<OrderItem>
var invalidQuantityItems = message.ItemsForReturn.Where(returnItem =>
{
var matchingOrderItemQuantity = message.OrderItems
.Where(orderItem => orderItem.ItemNumber.Equals(returnItem.ItemNumber) || orderItem.OrderItemId == returnItem.OrderItemId)
.Sum(orderItem => orderItem.Quantity);
return matchingOrderItemQuantity < returnItem.Quantity;
});
and the corresponding types of the variables used above:
public class ReturnItem
{
public int OrderItemId {get; set;}
public string ItemNumber {get; set;}
public int Quantity {get; set;}
// There's more properties but these are the ones that matter
{
public class OrderItem
{
public int OrderItemId {get; set;}
public string ItemNumber {get; set;}
public int Quantity {get; set;}
// There's more properties but these are the ones that matter
{
I expect that var invalidQuantityItems will be an IEnumerable<ReturnItems> whose quantity for an individual item is greater than the amount ordered (i.e. they're trying to return more than they ordered in the first place).
Cheers!
Small correction - the time complexity of the current implementation is O(N*M) and the best you can get is O(N+M).
The problem is how to efficiently correlate the two sets. In LINQ this is achieved with joins, and for this one-to-many type of correlation - group join. The equivalent of the || criteria will be Union of the results of two group joins (matching sets).
Speaking about readability, LINQ and joins, the best would be using the LINQ query syntax (there is a reason some people also call it comprehension syntax).
So the query in question can efficiently (and hopefully readable) be rewritten as follows:
var invalidQuantityItems =
from returnItem in message.ItemsForReturn
join orderItem in message.OrderItems on returnItem.ItemNumber equals orderItem.ItemNumber
into matchingOrderItems1
join orderItem in message.OrderItems on returnItem.OrderItemId equals orderItem.OrderItemId
into matchingOrderItems2
let matchingOrderItemQuantity = matchingOrderItems1.Union(matchingOrderItems2)
.Sum(orderItem => orderItem.Quantity)
where matchingOrderItemQuantity < returnItem.Quantity
select returnItem;
I think the dictionary approach is the best way to go.
About the readability, I think this should be not too bad:
var quantityByItemNumber = message.OrderItems.
Where(i => i.ItemNumber != null).
ToDictionary(
i => i.ItemNumber,
i => i.Quantity);
var quantityByOrderItemId = message.OrderItems.ToDictionary(
i => i.OrderItemId,
i => i.Quantity);
var invalidQuantityItems = message.ItemsForReturn.Where(returnItem =>
{
int matchingOrderItemQuantity;
var isNumberMatch = returnItem.ItemNumber != null) &&
quantityByItemNumber.TryGetValue(returnItem.ItemNumber, out matchingOrderItemQuantity);
if (!isNumberMatch)
quantityByOrderItemId.TryGetValue(returnItem.OrderItemId, out matchingOrderItemQuantity)
return matchingOrderItemQuantity < returnItem.Quantity;
});
In fact I think this is even more readable, because it does not wrongly pretend that there is more than one matching OrderItem, which quantities has to be summed up.
As far as optimizing multiple conditions goes:
Always put the most likely conditions that will end evaluation first (you would have to determine this based on existing data or your knowledge of the system).
If there is not a large likelihood of one condition occurring more frequently than the other, then we can consider the evaluation itself. For example, if an int comparison is faster than a string comparison, then put the int comparison first.
Also, your code doesn't need a separate line to get the Sum; you can do it in the same expression:
var invalidQuantityItems = message.ItemsForReturn.Where(returnItem =>
message.OrderItems
.Where(orderItem =>
orderItem.OrderItemId == returnItem.OrderItemId ||
orderItem.ItemNumber.Equals(returnItem.ItemNumber))
.Sum(orderItem => orderItem.Quantity) < returnItem.Quantity);

Finding the list of common objects between two lists

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));

Combine Two Properties From Entity In List And Flatten It With Linq

I have a list of entities in this structure below. How can I create a distinct list of List<int> of all the AwayTeamId and HomeTeamId with linq in one call? I know I can do a select many on the HomeTeamId and get all of those, but I also need the AwayTeamId included.
class Game {
int AwayTeamId;
int HomeTeamId;
}
Uriil's answer may be even shorter:
var result = games
.SelectMany(game => new[] { game.AwayTeamId, game.HomeTeamId })
.Distinct()
;
No need for additional .Select and lists creation for every Game record.
Assuming you are just after a flat list of all the team ids (home or away), then how about UNIONing two SELECTs?
var teamIds = games.Select(g => g.HomeTeamId).Union(games.Select(g => g.AwayTeamId));
[games being the list of Game entities in my example above]
This will work:
var result = entityContext.ListOfGames
.Select(p=>new List<int>{p.AwayTeamId, p.HomeTeamId})
.SelectMany(p=>p).Distinct();
If it's LINQ to ENTITY you will need to call .ToList() after ListOfGames, to make this solution works

Sort one list by another

I have 2 list objects, one is just a list of ints, the other is a list of objects but the objects has an ID property.
What i want to do is sort the list of objects by its ID in the same sort order as the list of ints.
Ive been playing around for a while now trying to get it working, so far no joy,
Here is what i have so far...
//**************************
//*** Randomize the list ***
//**************************
if (Session["SearchResultsOrder"] != null)
{
// save the session as a int list
List<int> IDList = new List<int>((List<int>)Session["SearchResultsOrder"]);
// the saved list session exists, make sure the list is orded by this
foreach(var i in IDList)
{
SearchData.ReturnedSearchedMembers.OrderBy(x => x.ID == i);
}
}
else
{
// before any sorts randomize the results - this mixes it up a bit as before it would order the results by member registration date
List<Member> RandomList = new List<Member>(SearchData.ReturnedSearchedMembers);
SearchData.ReturnedSearchedMembers = GloballyAvailableMethods.RandomizeGenericList<Member>(RandomList, RandomList.Count).ToList();
// save the order of these results so they can be restored back during postback
List<int> SearchResultsOrder = new List<int>();
SearchData.ReturnedSearchedMembers.ForEach(x => SearchResultsOrder.Add(x.ID));
Session["SearchResultsOrder"] = SearchResultsOrder;
}
The whole point of this is so when a user searches for members, initially they display in a random order, then if they click page 2, they remain in that order and the next 20 results display.
I have been reading about the ICompare i can use as a parameter in the Linq.OrderBy clause, but i can’t find any simple examples.
I’m hoping for an elegant, very simple LINQ style solution, well I can always hope.
Any help is most appreciated.
Another LINQ-approach:
var orderedByIDList = from i in ids
join o in objectsWithIDs
on i equals o.ID
select o;
One way of doing it:
List<int> order = ....;
List<Item> items = ....;
Dictionary<int,Item> d = items.ToDictionary(x => x.ID);
List<Item> ordered = order.Select(i => d[i]).ToList();
Not an answer to this exact question, but if you have two arrays, there is an overload of Array.Sort that takes the array to sort, and an array to use as the 'key'
https://msdn.microsoft.com/en-us/library/85y6y2d3.aspx
Array.Sort Method (Array, Array)
Sorts a pair of one-dimensional Array objects (one contains the keys
and the other contains the corresponding items) based on the keys in
the first Array using the IComparable implementation of each key.
Join is the best candidate if you want to match on the exact integer (if no match is found you get an empty sequence). If you want to merely get the sort order of the other list (and provided the number of elements in both lists are equal), you can use Zip.
var result = objects.Zip(ints, (o, i) => new { o, i})
.OrderBy(x => x.i)
.Select(x => x.o);
Pretty readable.
Here is an extension method which encapsulates Simon D.'s response for lists of any type.
public static IEnumerable<TResult> SortBy<TResult, TKey>(this IEnumerable<TResult> sortItems,
IEnumerable<TKey> sortKeys,
Func<TResult, TKey> matchFunc)
{
return sortKeys.Join(sortItems,
k => k,
matchFunc,
(k, i) => i);
}
Usage is something like:
var sorted = toSort.SortBy(sortKeys, i => i.Key);
One possible solution:
myList = myList.OrderBy(x => Ids.IndexOf(x.Id)).ToList();
Note: use this if you working with In-Memory lists, doesn't work for IQueryable type, as IQueryable does not contain a definition for IndexOf
docs = docs.OrderBy(d => docsIds.IndexOf(d.Id)).ToList();

Categories