I am trying to concate List<> as follows-
List<Student> finalList = new List<Student>();
var sortedDict = dictOfList.OrderBy(k => k.Key);
foreach (KeyValuePair<int, List<Student>> entry in sortedDict) {
List<Student> ListFromDict = (List<Student>)entry.Value;
finalList.Concat(ListFromDict);
}
But no concatenation happens. finalList remains empty. Any help?
A call to Concat does not modify the original list, instead it returns a new list - or to be totally accurate: it returns an IEnumerable<string> that will produce the contents of both lists concatenated, without modifying either of them.
You probably want to use AddRange which does what you want:
List<Student> ListFromDict = (List<Student>)entry.Value;
finalList.AddRange(ListFromDict);
Or even shorter (in one line of code):
finalList.AddRange((List<Student>)entry.Value);
And because entry.Value is already of type List<Student>, you can use just this:
finalList.AddRange(entry.Value);
Other answers have explained why Concat isn't helping you - but they've all kept your original loop. There's no need for that - LINQ has you covered:
List<Student> finalList = dictOfList.OrderBy(k => k.Key)
.SelectMany(pair => pair.Value)
.ToList();
To be clear, this replaces the whole of your existing code, not just the body of the loop.
Much simpler :) Whenever you find yourself using a foreach loop which does nothing but build another collection, it's worth seeing whether you can eliminate that loop using LINQ.
You may want to read up the documentation on Enumerable.Concat:
Return Value
Type: System.Collections.Generic.IEnumerable
An IEnumerable that contains the concatenated elements of the two input sequences.
So you may want to use the return value, which holds the new elements.
As an alternative, you can use List.AddRange, which Adds the elements of the specified collection to the end of the List.
As an aside, you can also achieve your goal with a simple LINQ query:
var finalList = dictOfList.OrderBy(k => k.Key)
.SelectMany(k => k.Value)
.ToList();
As specified here, Concat generates a new sequence whereas AddRange actually adds the elements to the list. You thus should rewrite it to:
List<Student> finalList = new List<Student>();
var sortedDict = dictOfList.OrderBy(k => k.Key);
foreach (KeyValuePair<int, List<Student>> entry in sortedDict) {
List<Student> ListFromDict = (List<Student>)entry.Value;
finalList.AddRange(ListFromDict);
}
Furthermore you can improve the efficiency a bit, by omitting the cast to a List<T> object since entry.Value is already a List<T> (and technically only needs to be an IEnumerable<T>):
var sortedDict = dictOfList.OrderBy(k => k.Key);
foreach (KeyValuePair<int, List<Student>> entry in sortedDict) {
finalList.AddRange(entry.Value);
}
Concat method does not modify original collection, instead it returns brand new collection with concatenation result. So, either try finalList = finalList.Concat(ListFromDict) or use AddRange method which modifies target list.
Related
I have a list that I want to put in a dictionary, for simplicity the values being inserted will all be the same.
I can use a foreach loop.
List<string> list = new List<string>();
list.Add("Earth");
list.Add("Wind");
list.Add("Fire");
list.Add("Water");
list.Add("Water"); // Will NOT BE INSERTED using the foreach loop
var myDictionary= new Dictionary<string, int>();
foreach (string value in list)
{
if (!myDictionary.ContainsKey(value))
{
myDictionary.Add(value, 1);
}
}
The above works.
But I want to use ToDictionary do the same in the following way -
Dictionary<string, int> myDictionary2 = list.ToDictionary(i => i, i => 1);
Of course this fails because I'm adding "Water" twice.
What is the correct way of checking for duplicate entries when using ToDictionary?
You could use Distinct() to filter out duplicates:
Dictionary<string, int> myDictionary2 = list.Distinct().ToDictionary(i => i, i => 1);
The same approach would make your traditional loop much clearer too, since you don't have to check "manually" for duplicates:
foreach (string value in list.Distinct())
{
myDictionary.Add(value, 1);
}
Distinct is one option that avoids the duplicate key issue. If you need a count of duplicates, you might try something more like this GroupBy as follows:
var dict = list.GroupBy(i => i).ToDictionary(g => g.Key, g => g.Count());
If your application is not just a simple string-list/duplicate-count structure, you might get some mileage from choosing a different structure like a Lookup that you can get from calling the ToLookup extension -or possibly going with a Grouping like the GroupBy I used above.
I have two lists
List<T> list1 = new List<T>();
List<T> list2 = new List<T>();
I want remove all elements from list1, which also exist in list2. Of course I can loop through the first loop looking for each element in list2, but I am looking for elegant solution.
Thanks!
To change the actual list1 in place, you could use
list1.RemoveAll(item => list2.Contains(item));
You might instead prefer to simply have a query over the lists without modifying either
var result = list1.Except(list2);
LukeH makes a good recommendation in the comments. In the first version, and if list2 is particularly large, it might be worth it to load the list into a HashSet<T> prior to the RemoveAll invocation. If the list is small, don't worry about it. If you are unsure, test both ways and then you will know.
var theSet = new HashSet<YourType>(list2);
list1.RemoveAll(item => theSet.Contains(item));
With LINQ:
var result = list1.Except(list2);
list1.RemoveAll( item => list2.Contains(item));
Description
I think you mean the generic type List<Type>. You can use Linq to do this
Sample
List<string> l = new List<string>();
List<string> l2 = new List<string>();
l.Add("one");
l.Add("two");
l.Add("three");
l2.Add("one");
l2.Add("two");
l2.Add("three");
l2.Add("four");
l2.RemoveAll(x => l.Contains(x));
More Information
MSDN - List.RemoveAll Method
var result = list1.Except(list2);
Using LINQ you can do this:
List1.RemoveAll(i => !List2.Contains(i));
If you want to remove a list of objects (list2) from another list (list1) use:
list1 = list1.Except(list2).ToList()
Remember to use ToList() to convert IEnumerable<T> to List<T>.
var NewList = FirstList.Where(a => SecondList.Exists(b => b.ID != a.ID));
Using LINQ
So I a collection of dictionary items in a list:
List<Dictionary<string, string>> inputData = new List<Dictionary<string, string>>(inputs);
List<Dictionary<string, string>> itemStack = new List<Dictionary<string, string>>();
Now what I want to do is for each inputData dictionary item I want to check if itemStack has the same value (Dictionary Item) already.
I was thinking it would be like?
foreach (var item in inputData)
{
if(!itemStack.Contains(item){ itemStack.Add(item)}
else{ //Duplicate found}
}
It doesn't really check the items values inside? It just assumes that it doesn't have it...
All i want is if itemStack contains and item that is already in the stack don't include it.
I know I'm missing something obvious.
Thanks,
Dictionary is reference type, so it doesn't check the "deep" value like you expected.
You will have to write your own "Contains" method, either as totally separate method or extension of the Dictionary itself then use it instead, for example:
if(!MyContains(itemStack, item)){ itemStack.Add(item)}
True that a HashSet would be better, but if you want to do it here, try this (assuming you are filtering duplicate keys only):
foreach (var item in inputData.Keys)
{
if (itemStack.Where(x => x.Key == item.Key).Count() > 0)
// There was a duplicate
}
Or, if you only care when the data is coming out you can call:
itemStack.Distinct()
I think, your way is right. On my mind, HashSet is good, but when you add a new element, it performs the same test on the contents of the same items.
Regards.
Based on your initial problem statement, you might do something like this:
var aggregateKnownKeys = itemStack.SelectMany(d => d.Keys);
itemStack.AddRange(
inputData.Select(d=> d.Where(p => !aggregateKnownKeys.Contains(p.Key))
.ToDictionary(p => p.Key, p => p.Value)));
If you only need to combine two dictionaries then you could do this to skip keys that exist in itemStack:
var inputData = new Dictionary<string, string>();
var itemStack = new Dictionary<string, string>();
var oldStack = itemStack;
itemStack = new[] { inputData.SkipWhile(d => oldStack.Keys.Contains(d.Key)), itemStack }
.SelectMany(d => d)
.ToDictionary(d => d.Key, d => d.Value);
Okay so this isn't quite a full answer but it's what I did.
So I have a List of items and instead of doing a full compare to whats in an List(Hence the other considered) I just did a single item check:
if(!String.IsNullOrEmpty(item["itemId"]))
{
alert.DaleksApproaching(item["itemId"]);
}
So when it does see it has a value it just does another event to get rid of it.
The idea of using LINQ and the method approaches about(Contains and Distinct)I like. I have yet to try that, but I plan on doing that. For this it doesn't use LINQ :(
Thanks everyone!
(highly simplified example)
I have a generic list of strings:
var strings = new List<string> { "abc", "owla", "paula", "lala", "hop" };
I'm looking for the most efficient way to split this list into a list with elements that meet a condition and a list of elements that don't meet that same condition.
Func<string, bool> condition = s => s.IndexOf("o") > -1;
Predicate<string> kickOut = s => s.IndexOf("o") > -1;
var stringsThatMeetCondition = strings.Where(condition);
strings.RemoveAll(kickOut);
var stringsThatDontMeetCondition = strings;
Is there a way to do this with looping only once through the original list?
Use some linq:
var matches = list.Select(s => s.IndexOf("o") > -1).ToList();
var notMatches = list.Except(matches).ToList();
list.Clear();
list.AddRange(matches);
Update: as has been mentioned in the comments, be careful mutating the list as linq methods try to be on-demand, they will not iterate the list until you start looking into the IEnumerable. However in my case, I call ToList, which effectively causes it to run through the entire list of items.
This would do it:
IEnumerable<T> FilterAndRemove(this List<T> list, Func<T, bool> pred)
{
List<T> filtered = new List<T>();
int i = 0;
while(i < list.Count)
{
if (pred(list[i]))
{
filtered.Add(list[i]);
list.RemoveAt(i);
}
else
{
++i;
}
}
return list;
}
But am sure you have already thought of something similar. Can you please update your answer with the kind of efficiency that you seek?
Note that two filtering runs with pred and !pred over the original list would still be O(n) and not at all inefficient. Especially considering that you'd get the full benefit of lazy evaluation for both result sets. See also Rob's answer.
This algorithm is in O(n^2).
Instead removing each element, you can also collect them in a new list and copy them over to the input list before returning. This will also get you O(n).
One more option for O(n) would be switching to a linked list.
Why not just use
var stringsThatMeetCondition = strings.Where(condition);
var stringsThatDontMeetCondition = strings.Where(x => !condition(x));
Of course, you end up applying the condition to each element in the list twice. To avoid this you might want to write a generic splitting function, which wouldn't be as neat.
Func<string, bool> condition = ...;
var groupedStrings = strings.GroupBy(condition)
var stringsMeetingCondition = groupedStrings.FirstOrDefault(g => g.Key);
var stringsNotMeetingCondition = groupedStrings.FirstOrDefault(g => !g.Key);
Is there a way to "convert" (return) an IEnumerable list of, e.g., strings to an IEnumerable list of a different type when that different type accepts the former type in its constructor?
For example, the DataTable.Columns.AddRange() method accepts only lists of columns. Is there a way to return a DataColumn list by offering a string list using LINQ or some sort of aggregate function? I imagine the code would roughly do the following, but in one line:
var columnList = new List<DataColumn>();
foreach (var item in myStringList)
{
columnList.Add(item);
}
return columnList;
Likewise, is there an aggregate method that will take a list and run each of its members against a specific method? For example, I am looking for a one line way to perform the following similar foreach loop:
foreach (var item in myStringList)
{
myDataTable.Columns.Add(item);
}
Obviously, I am looking for generic answers that are not actually dependent on data columns or strings.
You can write
var newList = list.ConvertAll(x => new Something(x));
list.ForEach(x => DoSomething(x));
These methods are defined by th List<T> class.
If you have an arbitrary IEnumerable<T>, you can use LINQ:
var newEnumerable = enumerable.Select(x => new Something(x));
Call Enumerable.Aggregate
List<DataColumn> result = myStringList.Aggregate(
new List<DataColumn>(),
(list, item) => { list.Add(item); return list; }
);
return result;
That said, foreach statement is better.
Yes, in fact, although not all of them are LINQ specific. ForEach is just a List method. For your two examples:
myStringList.ForEach(x => columnList.Add(x));
// assumes myStringList is a List<T>... otherwise convert your enumerable using ToList()
The ForEach method takes an Action and lets you perform some logic on each item. So if you want to do transformations, it's easy enough combining with select:
myStringList.Select(x => new DataColumn(x))
.ToList()
.ForEach(x => columnList.Add(x));
// transforms each element of the string by adding some text, then calling foreach
// on the items
myStringList.ForEach(item => myDataTable.Columns.Add(item));
EDIT: that's not Linq. Sorry, my mistake.