I have a data structure like
public DespatchGroup(DateTime despatchDate, List<Products> products);
And I am trying to do...
var list = new List<DespatchGroup>();
foreach (var group in dc.GetDespatchedProducts().GroupBy(i => i.DespatchDate))
{
// group.Values is not correct... how do I write this?
list.Add(new DespatchGroup(group.Key, group.Values);
}
I'm obviously not understanding IGrouping as I can't see how to actually get to the data records within the group!
The group implements IEnumerable<T> - In the general case, just call foreach over the group. In this case, since you need a List<T>:
list.Add(new DespatchGroup(group.Key, group.ToList());
There's no Values property or similar because the IGrouping<T> itself is the IEnumerable<T> sequence of values. All you need to do in this case is convert that sequence to a list:
list.Add(new DespatchGroup(group.Key, group.ToList());
For any selected group,you could call
var selectedGroupValues=selectedGroup.SelectMany(x=>x);
Just a related tip - since, as the other answers have said, the grouping is an IEnumerable, if you need to access a specific index you can use group.ElementAt(i).
This is probably obvious to a lot of people but hopefully it will help a few!
Related
I am using this LINQ statement to sort a list by product name (ascending order) which contains product names (string) and Sizes available for each product (List<byte>);
LinkedList<FullItemDetails> itemDetails = new LinkedList<FullItemDetails>();
public class FullItemDetails
{
public string ProductName { get; set; }
public List<byte> Sizes { get; set; }
}
Now every time I input a new entry ex; Jacket,6,12,18,10, I think my program is sorting my list all over again;
itemDetails.AddLast(fullItemDetails);
//SortedProducts
itemDetails = Products.OrderBy(x => x.ProductName).ToList();
If the list is already sorted I only need to put the last entry in its correct place.
What is the best way to do it. Also to reduce the complexity of the algorithm. thanks
This seems like an ideal problem for a SortedList, as you have a key (name) and value (List<int> for the size).
Documentation is available here: http://msdn.microsoft.com/en-us/library/system.collections.sortedlist.aspx
The list declaration would look like this: SortedList<string, List<int> >. All inserts would be sorted on string, and the values can be enumerated based on each key.
Instead of List<T>, use SortedList<TKey, TValue> or SortedSet<T>. You can pass in an IComparer<T> to use a specific sorting algorithm through the respective constructor. Should you want to use a Lambda expression, you can use a small wrapper class to wrap a Comparison<T>.
Which will result in something like:
ICollection<FullItemDetails> _itemList = new SortedSet<FullItemDetails>(new ComparisonComparer<FullItemDetails>((x,y) -> x.ProductName.CompareTo(y.ProductName))
Your collection will now always be ordered.
When you're using .NET 4.5, you can use Comparer<T>.Create to create an IComparer implementation from a lambda expression.
You can use a SortedList<string,FullItemDetails>.
And you add your times like that list.Add(fullItemDetails.Name,fullItemDetails)
[Edit] : The order will be conserved after adding or removing an element.
[Edit2] Using LINQ
you use a list to store your items (adding/removing) : List<FullItemDetails> originalList and other property to read your sorted data :
IEnumerable<FullItemDetails> sortedList = originalList.OrderBy(e => e.Name).ThenBy(e => /* logic to order by another property*/);
and now you can iterate through your sortedList and as this sorted list is an IEnumerable<T> each time you iterate through it you will have exactly the same elements as in your originalList (after adding or removing items).
In other words : sortedList only contains the logic to read your originalList.
Hope this helps.
Regards.
I have an object that contains a list of child objects, each of which in turn contains a list of children, and so on. Using that first generation of children only, I want to combine all those lists as cleanly and cheaply as possible. I know I can do something like
public List<T> UnifiedListOfTChildren<T>()
{
List<T> newlist = new List<T>();
foreach (childThing in myChildren)
{
newlist = newlist.Concat<T>(childThing.TChildren);
}
return newlist;
}
but is there a more elegant, less expensive LINQ method I'm missing?
EDIT If you've landed at this question the same way I did and are new to SelectMany, I strongly recommend this visual explanation of how to use it. Comes up near the top in google results currently, but is worth skipping straight to.
var newList = myChildren.SelectMany(c => c.TChildren);
So, I have a List of objects of class A that contains a List of objects of class B
class A
{
...
List<B> bs;
}
and I have lists:
List<A> mainList;
List<B> listForRemoval;
How can I, using Linq, "clean" mainList, by removing all objects from bs (for every A in mainList) that exists in listForRemoval?
I hope I didn't confuse you with this question. :)
linq itself is probably not a great fit, but you can use some of it's extension methods. Linq typically is mostly for selection, not processing.
mainList.ForEach(x=>x.bs = x.bs.Where(y=>!listForRemoval.Contains(y)).ToList());
Yes, it's possible, as the other answers have shown. I would, however, choose the following solution which does not use LINQ at all:
foreach (var a in mainList) {
a.bs.RemoveAll(b => listForRemoval.Contains(b));
}
Advantages:
It's easier to read and understand.
It's not longer than the LINQ-based solutions---in fact, it's shorter than the accepted, LINQ-based answer.
It removes the elements from bs rather than assigning a new list to bs. This might yield better performance and/or be necessary if the list is used in other places as well.
foreach (var list in mainList) {
list.bs = list.bs.Where(b => !listForRemoval.Contains(b)).ToList();
}
mainList.ForEach(a => a.bs.RemoveAll(b => listForRemoval.Contains(b)));
I have a collection of anonymous class and I want to return an empty list of it.
What is the best readable expression to use?
I though of the following but I don't think they are readably enough:
var result = MyCollection.Take(0).ToList();
var result = MyCollection.Where(p => false).ToList();
Note: I don't want to empty the collection itself.
Any suggestion!
Whats about:
Enumerable.Empty<T>();
This returns an empty enumerable which is of type T. If you really want a List so you are free to do this:
Enumerable.Empty<T>().ToList<T>();
Actually, if you use a generic extension you don't even have to use any Linq to achieve this, you already have the anonymous type exposed through T
public static IList<T> GetEmptyList<T>(this IEnumerable<T> source)
{
return new List<T>();
}
var emp = MyCollection.GetEmptyList();
Given that your first suggestion works and should perform well - if readability is the only issue, why not create an extension method:
public static IList<T> CreateEmptyCopy(this IEnumerable<T> source)
{
return source.Take(0).ToList();
}
Now you can refactor your example to
var result = MyCollection.CreateEmptyCopy();
For performance reasons, you should stick with the first option you came up with.
The other one would iterate over the entire collection before returning an empty list.
Because the anonymous type there is no way, in source code, to create a list. There is, however, a way to create such list through reflection.
I am using C# and targeting the .NET Framework 3.5. I'm looking for a small, succinct and efficient piece of code to copy all of the items in a ListBox to a List<String> (Generic List).
At the moment I have something similar to the below code:
List<String> myOtherList = new List<String>();
// Populate our colCriteria with the selected columns.
foreach (String strCol in lbMyListBox.Items)
{
myOtherList.Add(strCol);
}
Which works, of course, but I can't help but get the feeling that there must be a better way of doing this with some of the newer language features. I was thinking of something like the List.ConvertAll method but this only applies to Generic Lists and not ListBox.ObjectCollection collections.
A bit of LINQ should do it:-
var myOtherList = lbMyListBox.Items.Cast<String>().ToList();
Of course you can modify the Type parameter of the Cast to whatever type you have stored in the Items property.
The following will do it (using Linq):
List<string> list = lbMyListBox.Items.OfType<string>().ToList();
The OfType call will ensure that only items in the listbox's items that are strings are used.
Using Cast, if any of the the items are not strings, you will get an exception.
How about this:
List<string> myOtherList = (from l in lbMyListBox.Items.Cast<ListItem>() select l.Value).ToList();
What about:
myOtherList.AddRange(lbMyListBox.Items);
EDIT based on comments and DavidGouge's answer:
myOtherList.AddRange(lbMyListBox.Items.Select(item => ((ListItem)item).Value));
You don't need more. You get List of all values from Listbox
private static List<string> GetAllElements(ListBox chkList)
{
return chkList.Items.Cast<ListItem>().Select(x => x.Value).ToList<string>();
}