I have a list with elements that I wanted to group by the value of a property. Later I would like to create a dictionary which key is this value that I used to group and the value a list (or IEnumerable) of the elements that are each group.
I am trying something like that:
Dictionary<long, Ienumerable<MyType>> dic = lstWithElements.GroupBy(x=>x.ID).ToDictionary(x=>x.????)
But in the ToDictionary method I don't have the ID property. So, how could I create my dictionary with the grouped items?
The overload of GroupBy that you're using returns an IEnumerable<IGrouping<long, MyType>>. IGrouping<long, MyType> provides a Key property of type long, representing the projected value by which elements were grouped, and also implements IEnumerable<MyType>.
So essentially, what you need is:
var dic = lstWithElements.GroupBy(x => x.ID).ToDictionary(x => x.Key);
Note: As pointed out in comments, this produces an IDictionary<long, IGrouping<long, MyType>>. This isn't really a problem, as long as you're only retrieving elements from the dictionary, and not trying to add new IEnumerable<MyType>s later on (which seems unlikely). If you do need precisely an IDictionary<long, IEnumerable<long, MyType>>, use the code outlined in this answer.
The ToDictionary method has a couple of overloads, but since your Dictionary uses an IEnumerable<MyType> for its Value, you're probably interested in the overload that accepts two parameters: a key selector, and an element selector.
Dictionary<long, IEnumerable<MyType>> dic = lstWithElements.GroupBy(x=>x.ID).ToDictionary(x=> x.Key, x => x.AsEnumerable());
Try this:
Dictionary<long, IGrouping<long,MyType>> dic = lstWithElements.GroupBy(x=>x.ID).ToDictionary(x=>x.Key)
I presume, that your data structure is at minimum this:
class MyType //or struct
{
long ID;
};
You want a list:
List<MyType> list;//with instances of MyType
either with different instances of MyType and same ID(making ID non-unique, is not best design perhaps) or some instances are in the list multiple times, what seems to be a better case, but either will work for question asked.
Now, GroupBy, what it does? List
List<MyType>
is transformed to
IEnumerable<MyType>
then GroupBy(x => x.ID) is grouping and providing:
IEnumerable<IGrouping<long, MyType>>
so we get elements of
IGrouping<long, MyType>
Now IGrouping knows everything IEnumerable does, interface inheritance, plus it has Key. So if you want your expected dictionary type:
Dictionary<long,IEnumerable<MyType>>
you have to do this:
var dictionary =
list
.GroupBy(x => x.ID)
.ToDictionary(x => x.ID, x => x.AsEnumerable())
;
ToDictionary allows to chose the Key from elements and also allows to transform Value stored for given key, so we can use this approach and call
x.AsEnumerable()
as IGrouping is inherited from IEnumerable.
Hope this longer explanation helps :).
Related
I have a IEnumerable<T> list. - T is a custom type, one of the members of that type is an int called Id.
I also have a Dictionary<int, int>object the first int is an Id (this id will be in the IEnumerable<T> list. The second Int is a rating number.
I want to order the IEnumerable<T> list based on the rating inside Dictionary<int, int> - Highest First
I can do this by if statements and looping, but I have a feeling this can be done using a linq/lambda statement.
Any one got any ideas how this can be done?
I'm guessing something like list.OrderBy(x => x.id == Dictionaryname. something something something
just use the OrderByDescending
var sorted = list.OrderByDescending(x => dictionary[x.id])
You can simply do:
list.OrderByDescending(x => dictionary[x.id])
I have the following code working with Tuples. Input is list of items, output is list of tuples and we need to calculate number of items for each date basically.
List<Tuple<DateTime, int>> list = new List<Tuple<DateTime, int>>();
foreach (ItemClass item in items)
{
foreach(Tuple<DateTime, int> tuple in list)
{
if (tuple.Item1 == item.date)
{
tuple.Item2++;
continue;
}
}
list.Add(Tuple.Create<DateTime, int>(item.date, 1));
}
This code currently doesn't compile because Item2 is read-only, the question is how to make it work?
Earlier this worked with the Dictionary but I had to remove it because it was not acceptable for outer code to work with the Dictionary.
Tuples are not intended for use in scenarios where mutability is required. You could make your own class that combines a DateTime with a mutable integer counter, but you can also do it with LINQ, converting to a list of tuples at the very end:
var list = items
.GroupBy(item => item.date)
.Select(g => Tuple.Create(g.Key, g.Count()))
.ToList();
The above code creates a group for each date, and then produces tuples only when the final counts of items in each group are known.
Try using Linq, GroupBy()to group by date, then use Select() and create a tuple for each group and finally convert to a list using ToList(). Something like
var result = items.GroupBy(x => x.Date)
.Select(x => Tuple.Create(x.Key, x.Count()))
.ToList();
I'm assuming because it's read only it already has a property, I think I've used tuple before so yeah, it probably does.
maybe you can't edit it because you can edit iterators in the Foreach() loop, perhaps experiment with another kind of loop.
OR
Set the item2 object to an object outside of the current loop and use that instead of the iterator.
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
IDictionary<string,object> d1;
Dictionary<string, object> d2;
I need to remove from d1 all entries that are not in d2.
I know I can do this with a for loop etc but that's so last century; I want to do it right.
I got to
d1.Where(x => {return d2.ContainsKey(x.key);});
but dont know what to do next
LINQ isn't designed to modify existing elements - but you could always create a new dictionary. For example:
d1 = d1.Where(x => d2.ContainsKey(x.Key))
.ToDictionary(x => x.Key, x => x.Value);
Or:
d1 = d1.Keys.Intersect(d2.Keys)
.ToDictionary(key => x.Key, key => d1[key]);
As others have said, if you're more keen on doing a Remove operation, I'd just loop. For example:
foreach (var key in d1.Keys.Except(d2.Keys).ToList())
{
d1.Remove(key);
}
(I'm not sure why you used a statement lambda in your sample code, by the way.)
LINQ is for querying. The information you're querying is the keys from d1 that are not in d2. Other than missing a NOT (unless you didn't mean to say "not" in your requirements). you already have that. When it comes to actually doing something, that's best done with a foreach loop, not LINQ:
foreach(var pair in d1.Where(x=> !d2.ContainsKey(x.Key)).ToList())
{
d1.Remove(pair.Key)
}
Note that the ToList is needed to ensure that you are not modifying a collection while iterating it.
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();