In the following code:
var cats = new List<string>() {"cat1", "cat2"};
var dogs = new List<string>() {"dog1", "dog2"};
var animals = new List<Animal>();
animals = (cats.Select(x => new Animal() {Type = "Cat", Name = x}).ToList().AddRange(
dogs.Select(x => new Animal() {Type = "Dog", Name = x}).ToList())).ToList();
Calling the ToList() at the end is an error, because AddRange() returns void. This doesn't seem right nowadays when using Linq type queries.
I found I could change it to .Union() or .Concat() to fix the issue, but shouldn't AddRange() be updated, or is there a reason for it returning void?
AddRange changes the underlying List object. No LinQ method does that. So it's fundamentally different and should not be used in a LinQ concatination. It's return value of void reflects that.
You've answered your own question. If you want distinct values from the two lists use Union, if you want to just join the two lists use Concat. Once the two enumerable have been joined you can call ToList().
AddRange is a method on the List its self and isn't anything to do with LINQ.
AddRange is a method on List<T> that pre-dates LINQ. It mutates the current list in situ and so doesn't need to return it (nor does it follow the fluent syntax style you find a lot these days). List<T> is not immutable, so mutating method calls are fine.
There are linq methods that can join lists together (as you've noted in the question). I would tend to not have mutating actions embedded in a linq method chain as it goes against the general idea that linq is just a query / projection set-up and doesn't "update" things.
In your case, it is better to use Enumerable.Concat:
animals = cats.Select(x => new Animal() {Type = "Cat", Name = x})
.Concat(dogs.Select(x => new Animal() {Type = "Dog", Name = x})).ToList();
Related
I have a list of tuples, the objects in the tuple are both the same type. The data structure of a list of tuples is needed until we get to error handling. To optimize error handling, I would like to flatten the tuples into a single list to allow for duplicate checking:
For instance if I had List<Tuple<string,string>>() (my types are more complex but the idea should hold):
[<"Tom","Dick">, <"Dick","Harry">, <"Bob","John">]
I would like to end up with:
["Tom", "Dick", "Harry", "Bob", "John"]
I know I could do this with:
List<string> stringList = List<string>();
Foreach(var item in tupleList){
stringList.Add(item.Item1);
stringList.Add(item.Item2);
}
stringList = stringList.Distinct();
But I am hoping for a more efficient way, perhaps something built into Linq. There is no guarantee of duplicates, but due to the performance cost of error handling, I would rather handle each only once.
If you need distinct items without order - use HashSet:
HashSet<string> stringList = new HashSet<string>();
foreach(var item in tupleList){
stringList.Add(item.Item1);
stringList.Add(item.Item2);
}
You can do similar code with LINQ, but it will not be faster (and probably not better looking as you need to convert Tuple to enumerable for most operations). You can try Aggregate if you really looking for LINQ.
Its simple by using linq SelectMany method
var tupleList = Enumerable.Range(0, 10).Select(i => new Tuple<string, string>(i.ToString(), "just for example")); // tuples
var trg = tupleList.SelectMany(t => new[] { t.Item1, t.Item2 }).Distinct();
One line, however still not a tetris in one line Ж)
As a slight variation on the above you could also do this:
HashSet<string> hash= new HashSet<string>();
tupleList.ForEach(l => hash.UnionWith(new string[] { l.Item1, l.Item2 }));
Although I like the SelectMany example in the comments of the question
I hope this is not a duplicate but I wasn't able to find an answer on this.
It either seems to be an undesired behavior or missing knowledge on my part.
I have a list of platform and configuration objects. Both contains a member string CodeName in it.
The list of CodeNames look like this:
dbContext.Platforms.Select(x => x.CodeName) => {"test", "PC", "Nintendo"}
dbContext.Configurations.Select(x => x.CodeName) => {"debug", "release"}
They are obtained from a MySQL database hence the dbContext object.
Here is a simple code that I was to translate in LINQ because 2 foreach are things of the past:
var choiceList = new List<List<string>>();
foreach (Platform platform in dbContext.Platforms.ToList())
{
foreach (Configuration configuration in dbContext.Configurations.ToList())
{
choiceList.Add(new List<string>() { platform.CodeName, configuration.CodeName });
}
}
This code gives my exactly what I want, keeping the platform name first which looks like :
var results = new List<List<string>>() {
{"test", "debug"},
{"test", "release"},
{"PC", "debug"}
{"PC", "release"}
{"Nintendo", "debug"}
{"Nintendo", "release"}};
But if I translate that to this, my list contains item in a different order:
var choiceList = dbContext.Platforms.SelectMany(p => dbContext.Configurations.Select(t => new List<string>() { p.CodeName, t.CodeName })).ToList();
I will end up with this, where the platform name isn't always first, which is not what is desired:
var results = new List<List<string>>() {
{"debug", "test"},
{"release", "test"},
{"debug", "PC"}
{"PC", "release"}
{"debug", "Nintendo"}
{"Nintendo", "release"}};
My question is, is it possible to obtain the desired result using LINQ?
Let me know if I'm not clear or my question lacks certain details.
Thanks
EDIT: So Ivan found the explanation and I modified my code in consequence.
In fact, only the Enumerable in front of the SelectMany needed the .ToList().
I should also have mentioned that I was stuck with the need of a List>.
Thanks everyone for the fast input, this was really appreciated.
When you use
var choiceList = dbContext.Platforms.SelectMany(p => dbContext.Configurations.Select(t => new List<string>() { p.CodeName, t.CodeName })).ToList();
it's really translated to some SQL query where the order of the returned records in not defined as soon as you don't use ORDER BY.
To get the same results as your nested loops, execute and materialize both queries, and then do SelectMany in memory:
var platforms = dbContext.Platforms.ToList();
var configurations = dbContext.Configurations.ToList();
var choiceList = platforms.SelectMany(p => configurations,
(p, c) => new List<string>() { p.CodeName, c.CodeName })
.ToList();
Rather than projecting it out to an array, project it out two a new object with two fields (potentially an anonymous object) and then, if you need it, project that into a two element array after you have retrieved the objects from the database, if you really do need these values in an array.
Try this-
var platforms= dbContext.Platforms.Select(x=>x.CodeName);
var configurations=dbContext.Configurations.Select(x=>x.CodeName);
var mix=platforms.SelectMany(num => configurations, (n, a) => new { n, a });
If you want to learn more in detail- Difference between Select and SelectMany
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 am doing the following...
//first I have a list of sports...
List<string> sports = new List<string> {"football","basketball"}
//then I have a list of partners who are offering that sport
List<Partner> = Partner.GetAvailablePartners();
//Partner is a type that has a List<string> Sports members.
Now what I want to do is filter my list of Partners so that only partners who are offering the sports contained in sports but I can't figure out how.
I thought could use the Intersection extension method like this...
var filteredPartners = (List<Partner>) (from p in partners where p.Sports.Intersect(sports).Count()>0 select p);
but I it doesn't work I guess because I am acting on a member list not the root list.
Can this be done with the intersect (or some other linq) method?
Seth
If you really need the results in a List then call ToList() at the end of your LINQ statement. Otherwise, you should be able to do:
IEnumerable<Partner> partners = partners.Where(p => p.Sports.Intersect(sports).Count() == sports.Count);
...or in a resultant List:
List<Partner> partners = partners.Where(p => p.Sports.Intersect(sports).Count() == sports.Count).ToList();
.Where() and all other lINQ methods return an IEnumerable<T> with lazy iteration, not List<T>.
You can't cast them to List<T>.
If you want a List<T>, call ToList().
Use intersect.
Example using two IEnumerables
var ids = original.Select(x => x.ID).Intersect(yourEnumerable);
What am I missing here?
I want to do a simple call to Select() like this:
List<int> list = new List<int>();
//fill the list
List<int> selections = (List<int>)list.Select(i => i*i); //for example
And I keep having trouble casting it. What am I missing?
Select() will return you an IEnumerable<int> type, you have to use the ToList() operator:
List<int> selections = list.Select(i => i*i).ToList();
Select() doesn't return a List so of course you can't cast it to a list.
You can use the ToList method instead:
list.Select(i => i*i).ToList();
As others have said, Select returns an IEnumerable<T> which isn't actually a list - it's the result of a lazily-evaluated iterator block.
However, if you're dealing with lists and you want a list back out with nothing other than a projection, using List<T>.ConvertAll will be more efficient as it's able to create the new list with the right size immediately:
List<int> selections = list.ConvertAll(i => i*i);
Unless you particularly care about the efficiency, however, I'd probably stick to Select as it'll give you more consistency with other LINQ code.