I am trying to do a simple EF 6.0 query, but it seems it am getting nowhere and banging my head with it.
I am querying to take a single row as an object, but this return two dimentional array like below;
[
[],
[],
[{
Id: 1,
Name: Abc
}]
]
What I would like to return is the object.
{
Id: 1,
Name: Abc
}
and here is the query
var q = _operatorAssessmentContext.OperatorAssessmentQuestions
.Select(x => x.ConditionalQuestions.Where(c => c.Id == questionId)).ToList();
So the whole idea is to change that two dimentional list into an object.
First off, you say you want an object, but your variable, q, will be a list. I'm gonna assume you want an object, as that is what you stated.
Couple ways you can do this:
You can flatten the list, and then take one.
You can filter away empty lists in your list, and then take the first object of the first list in your list.
Personally, I would prefer the first, and the query going in to your variable, q, would look something like this:
var q = _operatorAssessmentContext
.OperatorAssessmentQuestions
.SelectMany(x => x.ConditionalQuestions.Where(c => c.Id == questionId))
.FirstOrDefault();
Note the only difference is me using .SelectMany(), which will take, as an input, a list of lists holding elements of some type, T, and return one list holding elements of the same type, T.
Related
I have a list of Students, each student can enter one or more addresses.
I have to find either any of the addresses overlapped in the list and then retrieve those overlapping objects from the list.
below is the example of the list of objects
[
{
"ID" : 1,
"Addresses": ["SRJ", "SJ"]
},
{
"ID" : 2,
"Addresses": ["SJ"}
},
{
"ID" : 3,
"Addresses": ["FRT", "FRI"}
},
{
"ID" : 4,
"Addresses": ["NR", "SJ"}
},
]
in the above list SJ is a repeated string in the three of the objects so, I have to return those three objects from the list with ID 1,2,4.
I have already done the above using loops but I am looking into the most efficient way of doing this task.
Assuming each list item in your question is mapped to a class like below:
public class AddressRecord
{
public int ID { get; set; }
public List<string> Addresses { get; set; }
}
You can then use a Linq expression like below to find duplicates and construct an object that tracks all duplicate addresses and IDs those addresses belong to:
var result = list.SelectMany(x => x.Addresses).GroupBy(x => x).Where(x => x.Count() > 1)
.Select(x => new { x.Key, IDs = list.Where(y => y.Addresses.Contains(x.Key)).Select(y => y.ID).ToList() })
.ToList()
First line in linq expression flattens the address list, runs a groupby expression on them and finds all addresses that exist more than once (across all "addresses" lists.)
Then the second line select each address with duplicates and IDs corresponding to that address. (Note that x in x.Key is the IGrouping object created by the groupby expression and x.Key is the address that the grouping was created on.)
The result object should look something like this when serialized:
[{"Key":"SJ","IDs":[1,2,4]}]
Runtime performance wise, a handmade for loop would most certainly beat this expression. However, from maintainability perspective, this linq expression is likely much easier to manage. (This does depend on comfort level of team with linq.)
I assume that you have a given address and want to check if that exists somewhere else:
string givenAddress = "SJ";
List<Student> overlappingAddressesStudents = students
.Where(s => s.Addresses.Contains(givenAddress))
.ToList();
This might not be more efficient that your loop approach, but maybe it's more readable.
I have a deserialized Json object that I am trying to filter before processing. The data looks like this...
Company Division LastModDate Lot's of other columns/objects
123 1 7/1/2021
123 1 8/1/2022
123 2 8/1/2022
How can I get all the information in the original object and get rid of records that are not the latest for each Company/Division group?
I tried this...
var filtered = origObject.GroupBy(g=> new {g.Company,g.Division})
I don't know where to go next.
If I were doing this in SQL then I would be using row_number and just taking the 1 for example.
You could try something like
var filtered = origObject
.GroupBy(x => new {g.Company,g.Division})
.Select(g => g.OrderByDescending(x => x.LastModDate).First());
This will select one latest object from each group.
Edit: I'm not sure without a compiler at hand if this will group correctly - your grouping key is an anonymous object, I don't remember if they have any equality comparer other than by reference. You could try using a record instead, records have equality by value of all their properties - .GroupBy(g => (g.Company,g.Division)). Or just group by a string key such as $"{g.Company},{g.Division}",
A much more efficient way of doing this is as follows:
var filtered = origObject
.OrderByDescending(w => w.LastModDate)
.DistinctBy(w => (w.Company, w.Division));
This avoids the heavy array allocation and copying of GroupBy, especially since you only care about one item from its result.
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 have a list of strings and I'd like to order them.
IEnumerable<String> strings = ...;
strings = strings.OrderBy(a => a);
What I don't get is the point of the lambda expression a => a in there. First I thought that I can pull out a property and order at the same like like this.
IEnumerable<Something> somethings = ...;
IEnumerable<String> strings = somethings.OrderBy(a => a.StringProperty);
But that doesn't compile. So I'll have to go like this.
IEnumerable<Something> somethings = ...;
IEnumerable<String> strings = somethings.Select(a
=> a.StringProperty).OrderBy(a => a);
So why am I enforced to use the lambda expression in the OrderBy command?!
The lambda indicates the "what you want to order by".
If you take a set of people, and order them by their birthday, you still have a set of people - not a set of birthdays; i.e.
IEnumerable<Person> people = ...;
IEnumerable<Person> sorted = people.OrderBy(a => a.DateOfBirth);
so similarly, ordering a set of Somethings by StringProperty still results in a set of Somethings:
IEnumerable<Something> somethings = ...;
IEnumerable<Something> sorted = somethings.OrderBy(a => a.StringProperty);
In some (very few) cases, you do actually mean "and order it by the thing itself". This usually applies only to things like IEnumerable<string> or IEnumerable<int> - so the minor inconvenience of .OrderBy(x => x) is trivial. If it bothers you, you could always write an extension method to hide this detail.
When you order a collection it doesn't change it's type, hence
IEnumerable<Something> somethings = ...;
var strings = somethings.OrderBy(a => a.StringProperty);
results in an IEnumerable<Something>, you have to select the property to change the type:
IEnumerable<String> strings = somethings
.OrderBy(s => s.StringProperty)
.Select(s => s.StringProperty);
So why am I enforced to use the lambda expression in the OrderBy
command?!
Because Enumerable.OrderBy is a method that needs an argument.
Because you're not selecting it, you're ordering by it.
Try this:
Console.WriteLine(string.Join(", ",
new[] { new { Int = 1 }, new { Int = 2 }, new { Int = 0 }
.OrderBy(a => a.Int));
This will give you the lists, ordered by the Int property, not just randomly ordered!
This means that you can order by any property of the object, instead of just the object itself.
the structure of the .OrderBy(TSource, TKey) method has a requirement for both the Source item and the item to sort by. the lambda is saying "Order TSource using TKey", or in your case, "Order a using a"
The purpose of the parameter lambda in OrderBy is precisely tell the criteria using for ordering. It takes an object you're sorting, and returns another "thing" (same type or not) which will be sorted, sort of extracting a key to be sorted from the original source.
Your first sample is really trivial, and your rant is somewhat justified there, since if you start from a list of strings, you most likely will want to sort by those strings precisely. Which makes me wonder too, why we can't have a parameterless OrderBy for those trivial cases.
For the second snippet:
IEnumerable<Something> somethings = ...;
IEnumerable<Something> strings = somethings.OrderBy(a => a.StringProperty);
Here is when the "sorting criteria" makes sense, as you order the objects by some property value derived from them, and not just for the objects themselves (which generally aren't comparable). The reason it doesn't compiles is in the second enumerable declaration, it should be an IEnumerable<Something> instead of IEnumerable<string>, because the ordering will return another list of the very same type as it received, but in a different order, regardless of sorting criteria.
In the third snippet, you solve that by Selecting the string property, that effectively yields a list of strings, but you lose all the input objects in the process. The lambda parameter is more or less pointless and trivial here, as you're starting from a plain string to begin with, the very same as the first sample.
Another way to use it would be to specify some different sorting criteria other than the trivial for strings. Say you want to sort not alphabetically, but by the third letter instead:
IEnumerable<String> strings = ...;
strings = strings.OrderBy(a => a.Substring(2, 1));
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();