C# - Combine multiple LINQ collections with same properties - c#

Maybe it's late in the night, but I'm stumped here. I'm trying to combine multiple lists with the same properties into one. I thought that LINQ's .UNION would do the trick but I was mistaken. Here's an example of a few of my lists:
LIST1 (report names):
Date Name Title Product
02/01/13 Steve Hello World Report
02/05/13 Greg Howdy Report
LIST2 (song names):
Date Name Title Product
01/01/13 John Time Song
01/05/13 Bob Sorry Song
LIST3 (games names):
Date Name Title Product
12/01/12 Google Bike Race Game
12/05/12 Apple Temple Run Game
My class is very simple. Here's what it looks like:
public class MyClass {
public DateTime Date { get; set; }
public string Name { get; set; }
public string Title { get; set; }
public string Product { get; set; }
}
In case you're wondering, I used this LINQ query to get one of the above lists:
var finalList = Games
.Select (s => new MyClass {
Date = (System.DateTime) s.Games.Creation_date,
Name = s.Games.Last_name,
Title = string.Format("{0} (Report)", s.Game.Headline),
Product="Report"
})
;
So far, it's pretty easy, but I want to combine all my lists into 1. So, my final list should look like:
Date Name Title Product
02/01/13 Steve Hello World Report
02/05/13 Greg Howdy Report
01/01/13 John Time Song
01/05/13 Bob Sorry Song
12/01/12 Google Bike Race Game
12/05/12 Apple Temple Run Game
I thought that a UNION command would do it:
var newList = List1.Union(List2).Union(List3);
But I'm not getting the desired output.
Date Name Title Product
02/01/13 Steve Hello World Report
02/05/13 Greg Howdy Report
01/01/13 Bob Time Game
01/05/13 John Sorry Song
12/01/12 Google Bike Race Song
12/05/12 Apple Temple Run Game
Any idea on what I'm doing wrong here?

Try:
list1.Concat(list2).Concat(list3);
You don't want to be using Union ( working or not) anyway as it does set union.

You could try using the AddRange command should look something like this
var FullList = list1.AddRange(list2).AddRange(list3);
or the fail safe way whould be
var FullList = list1.Concat(list2).Concat(list3).ToList(); //Personally i would use this
or you also have
var FullList = new[] { list1, list2, list3 }.SelectMany(a => GetAllProducts(a)).ToList();

Related

LINQ Count method : why the query is executed again?

I have read that the LINQ is very lazy...
I have a class with a model:
class Movie
{
public string Title { get; set; }
public float Rating { get; set; }
private int year;
public int Year
{
get
{
Console.WriteLine("Returning {0} for movie {1}", year, Title);
return year;
}
set
{
year = value;
}
}
}
And I have the following code:
var movies = new List<Movie>
{
new Movie {Title = "The Dark Knight", Rating = 8.9f, Year = 2008 },
new Movie {Title = "The King's Speech", Rating = 8.0f, Year = 2010 },
new Movie {Title = "Casablanca", Rating = 8.5f, Year = 1942 },
new Movie {Title = "Star Wars 5", Rating = 8.7f, Year = 1980 }
};
var query = movies.Where(m => m.Year > 2000);
Console.WriteLine( query.Count( ) );
foreach(var movie in query)
{
Console.WriteLine(movie.Title);
}
And here is the output:
Returning 2008 for movie The Dark Knight
Returning 2010 for movie The King's Speech
Returning 1942 for movie Casablanca
Returning 1980 for movie Star Wars 5
2
Returning 2008 for movie The Dark Knight
The Dark Knight
Returning 2010 for movie The King's Speech
The King's Speech
Returning 1942 for movie Casablanca
Returning 1980 for movie Star Wars 5
So, the Count method does not make the query to be executed? I see this as an opposite to an optimization... So is there a logical reason behind this? (The only reasonable reason that I see for this behaviour is CQS principle)
Thanks
Count did executed the query. those are the 4 lines before the "2".
the 6 lines after is the foreach. which rerun the query.
If what puzzle you is why the expression was evaluted twice. and by
So, the Count method does not make the query to be executed?
you mean, "Evaluate the conditions and saving the result". this is not how LINQ works.
Generaly speaking, when enumerating LINQ, for each element requested (MoveNext) LINQ is calculating only the new element. This way it doesn't process data that wasn't asked for.
Rerunning the same LINQ expression will go over the elements again and do the processing all over again.
Saving & reusing the result is on you. you can easily do it using ToArray() or ToList()

How to work with 2 models or more?

I have done some search here and also on the web but either I'm using wrong keywords or maybe most of the examples on MVVM deal with one model only.
I'm having two models in my project (self learning project on MVVM), song model and artist model. So far been able to bind a listview with a collection of information (from song), such that when user clicks on a row on listview information about a song are populated in few textbox controls.
The question I'm facing is that how can I communicate between two models? if we consider a model as a table with its column/fields then I should be able to create a reference to artist model (a foreign key) but what I'm not getting is that how I can retrieve information about an artist when I cilck on his song in the listview?
Long story short, I like to click on a row in listview which showing list of songs and then get its singer/artist pictures, his real name and etc. I'm not following the concept behind how to find related piece of data about an song in artist model.
Any advice will be apprecaited.
this is what I have now:
public class Song
{
string _singerId;
string _singerName;
string _songName;
string _songWriter;
string _genre;
int _songYear;
Artist artistReference;
Then I have:
public class Artist
{
string _artistBirthName;
string _artistNationality;
string _artistImageFile;
DateTime _artistDateOfBirth;
DateTime _artistDateOfDeath;
bool _isArtistAlive;
Thanks.
EDIT:
Here is how I provide the information:
Question is how can I insert Artist reference in Song collection?
Artists = new ObservableCollection<Artist>()
{
new Artist() { ArtistBirthName = "Francis Albert Sinatra", ArtistNickName = "Ol' Blue Eyes", ArtistNationality = "American", ... },
new Artist() { ArtistBirthName = "Elvis Aaron Presley", ArtistNickName = "", ArtistNationality = "American", ... },
new Artist() { ArtistBirthName = "James Paul McCartney", ArtistNickName = "", ArtistNationality = "British", ... },
new Artist() { ArtistBirthName = "Thomas John Woodward", ArtistNickName = "", ArtistNationality = "British", ... }
};
//later read it from xml file or a table.
Songs = new ObservableCollection<Song>()
{
new Song() {ARTIST INFO GOES HERE? HOW?, SingerName = "Fank Sinatra", SongName="Fly me to the Moon", SongWriterName="Bart Howard", Genre="Jazz" ,YearOfRelease= 1980 },
new Song() {SingerName = "Elvis Presley", SongName="Can't Help Falling in Love", SongWriterName="Paul Anka", Genre="Pop", YearOfRelease= 1969},
new Song() {SingerName = "The Beatles", SongName="Let It Be", SongWriterName="John Lennon", Genre="Rock", YearOfRelease= 1970},
new Song() {SingerName = "Tom Jones", SongName="Its Not Unusual", SongWriterName="Les Reed & Gordon Mills", Genre="Pop" , YearOfRelease= 1965}
};
I'm either missing something here or you're just looking for difficulties where there really are none. :) When creating a song object, just pass an artist to it. For example Artist artist1 = new Artist(...); Song song1 = new Song(..., artist1);
You'll of course want to define the constructors first.
EDIT: After your edit :)
You can do something like this:
using System.Linq; // For lambda operations
(...)
Songs = new ObservableCollection<Song>()
{
new Song() {Artist = Artists.FirstOrDefault(x => x.Name == "Francis Albert Sinatra"), SingerName = ...}
(...)
}
The Artists.FirstOrDefault(...) part is a LINQ query. It iterates over the Artists collection and selects the first item in the collection that matches the condition. If it doesn't find a matching item, it then uses a default value, which should be NULL. It would be better to give each artist a unique ID and search by it instead of name, though, as there can be more artists with the same name. Don't hesitate to ask if you have more questions!

Generic List is not coming

This sample uses where to find all products that are out of stock.
public void Linq2()
{
List<Product> products = GetProductList();
var soldOutProducts =
from p in products
where p.UnitsInStock == 0
select p;
Console.WriteLine("Sold out products:");
foreach (var product in soldOutProducts)
{
Console.WriteLine("{0} is sold out!", product.ProductName);
}
}
Result:
Sold out products:
Chef Anton's Gumbo Mix is sold out!
Alice Mutton is sold out!
Thüringer Rostbratwurst is sold out!
Gorgonzola Telino is sold out!
Perth Pasties is sold out!
The above example i got from MSDN Samples, this is Simple2, the problem is when I enter List<Products>, Products is not showing in Intellisense. When I enter it manually, I get the following error:
Only assignment, call, increment, decrement and new object expression
can be used as a statement
What can I do to solve this?
Ok,
your problem is that you copied the linked source. But this source does not contain the definitions for neither Product nor GetProductList()
Please take a look at the example here - it has everything you need:
List<string> fruits =
new List<string> { "apple", "passionfruit", "banana", "mango",
"orange", "blueberry", "grape", "strawberry" };
IEnumerable<string> query = fruits.Where(fruit => fruit.Length < 6);
foreach (string fruit in query)
{
Console.WriteLine(fruit);
}
That's because you don't have the class defined. You to add the class definition. You could write the class in the same file, add a new class file and put the definition of Product in it.

Linq to XML: I am not able to compare the nested element

Thank you in advance, this is a great resource.
I believe the code explains itself, but just in case I am being arrogant I will explain myself.
My program lists movies, to a treeview, according to the drop down lists selected genre. Each movie has a few genres, ergo the nested genres.
This is the XML:
<movie>
<title>2012</title>
<director>Roland Emmerich</director>
<writtenBy>
<writter>Roland Emmerich,</writter>
<writter>Harald Kloser</writter>
</writtenBy>
<releaseDate>12-Nov-2009</releaseDate>
<actors>
<actor>John Cusack,</actor>
<actor>Thandie Newton, </actor>
<actor>Chiwetel Ejiofor</actor>
</actors>
<filePath>H:\2012\2012.avi</filePath>
<picPath>~\image\2012.jpg</picPath>
<runningTime>158 min</runningTime>
<plot>Dr. Adrian Helmsley, part of a worldwide geophysical team investigating the effect on the earth of radiation from unprecedented solar storms, learns that the earth's core is heating up. He warns U.S. President Thomas Wilson that the crust of the earth is becoming unstable and that without proper preparations for saving a fraction of the world's population, the entire race is doomed. Meanwhile, writer Jackson Curtis stumbles on the same information. While the world's leaders race to build "arks" to escape the impending cataclysm, Curtis struggles to find a way to save his family. Meanwhile, volcanic eruptions and earthquakes of unprecedented strength wreak havoc around the world. </plot>
<trailer>http://2012-movie-trailer.blogspot.com/</trailer>
<genres>
<genre>Action</genre>
<genre>Adventure</genre>
<genre>Drama</genre>
</genres>
<rated>PG-13</rated>
</movie>
This is the code:
string selectedGenre = this.ddlGenre.SelectedItem.ToString();
XDocument xmldoc = XDocument.Load(Server.MapPath("~/App_Data/movie.xml"));
List<Movie> movies =
(from movie in xmldoc.Descendants("movie")
// The treeView doesn't exist
where movie.Elements("genres").Elements("genre").ToString() == selectedGenre
select new Movie
{
Title = movie.Element("title").Value
}).ToList();
foreach (var movie in movies)
{
TreeNode myNode = new TreeNode();
myNode.Text = movie.Title;
TreeView1.Nodes.Add(myNode);
}
Change your code to
List<Movie> movies =
(from movie in xmldoc.Descendants("movie")
where movie.Elements("genres").Elements("genre").Any(e => e.Value == selectedGenre)
select new Movie
{
Title = movie.Element("title").Value
}).ToList();
This is because there are more than 1 genre node, so you'll have to check if any of them match instead of just the first.
List<Movie> movies =
(from movie in xmldoc.Descendants("movie")
where movie.Elements("genres")
.Any((e) => e.Elements("genre").ToString() == selectedGenre);

Linq query preferences

Learning a bit about Linq.
I have the following code:
(Please excuse the pathetic size of the data set)
class Program
{
static void Main(string[] args)
{
var employees = new List<Employee>
{
new Employee
{
Name = "Bill Bailey",
EmployeeCode = 12345,
Department = "Comedy Lab",
DateOfBirth = DateTime.Parse("13/01/1964"),
CurrentEmployee = true
},
new Employee
{
Name = "Boris Johnson",
EmployeeCode = 56789,
Department = "Cycling Dept.",
DateOfBirth = DateTime.Parse("19/06/1964"),
CurrentEmployee = true
},
new Employee
{
Name = "Bruce Forsyth",
EmployeeCode = 5,
Department = "Comedy Lab",
DateOfBirth = DateTime.Parse("22/03/1928"),
CurrentEmployee = false
},
new Employee
{
Name = "Gordon Brown",
EmployeeCode = 666,
Department = "Backbenches",
DateOfBirth = DateTime.Parse("20/02/1951"),
CurrentEmployee = false
},
new Employee
{
Name = "Russell Howard",
EmployeeCode = 46576,
Department = "Comedy Lab",
DateOfBirth = DateTime.Parse("23/03/1980"),
CurrentEmployee = false
}
};
Func<Employee, bool> oapCalculator = (employee => employee.DateOfBirth.AddYears(65) < DateTime.Now);
var oaps1 = employees.Where(oapCalculator);
var oaps2 = (from employee in employees
where oapCalculator(employee)
select employee);
oaps1.ToList().ForEach(employee => Console.WriteLine(employee.Name));
oaps2.ToList().ForEach(employee => Console.WriteLine(employee.Name));
Console.ReadLine();
}
class Employee
{
public string Name { get; set; }
public int EmployeeCode { get; set; }
public string Department { get; set; }
public DateTime DateOfBirth { get; set; }
public bool CurrentEmployee { get; set; }
}
}
I have a few questions:
As far as I can tell, both of the featured Linq queries are doing the same thing (black magic may be afoot).
Would they both be compiled down to the same IL?
If not, why, and which would be the most efficient given a sizable amount of data?
What is the best way to monitor Linq query efficiency? Performance timers or something built-in?
Is the lambda expression the preferred method, as it is the most concise?
In a department of lambda fearing luddites, is it worth taking the plunge and teaching 'em up or using the SQL-esque syntax?
Thanks
Re
var oaps1 = employees.Where(oapCalculator);
vs
var oaps2 = (from employee in employees
where oapCalculator(employee)
select employee);
There is a slight difference, in particular around the where oapCalculator(employee). The second query is mapped to:
var oaps2 = employees.Where(employee => oapCalculator(employee));
so this is an extra layer of delegate, and will also incur the (small) overhead of a capture-class due to the closure over the variable oapCalculator, and a dereference of this per iteration. But otherwise they are the same. In particular, the Select is trivially removed (in accordance with the spec).
In general, use whichever is clearest in any scenario. In this case, either seems fine, but you will find it easier to use .Where etc if you are regularly dealing in scenarios that involving delegates or Expressions.
I don't mean this to be snide, but sometimes it is better to try things out for yourself. Along those lines, here are some tools, and some of my own experiences.
1 and 2: Disassemble and find out! :) http://www.red-gate.com/products/reflector/
3: Profile your app. This is the answer to any perf-determining question, unless you're doing algorithm work (mathematical proofs, big-o). Profiling tools are built into VS.
4: Which do you prefer? How about your co-workers? This sounds like a statistical question, which would require a survey
5: Similar to 4, try it and find out! As you may have experienced, evangelizing new techniques to your co-workers will teach you as much as it will teach them.
I've found I've had about a 50% success rate w/ teaching general delegate/lambda usage. I made sure to come up with practical examples from my production test code, and showed how the equivalent imperative code had lots of duplication.
I tried going through the free SICP videos with my team (being a really eye-opener on refactoring), and I found it a pretty hard sell. LISP isn't the most attractive language to the majority of programmers...
http://groups.csail.mit.edu/mac/classes/6.001/abelson-sussman-lectures/
Both LINQ queries are equivalent. The second uses syntactic sugar that the compiler translates to an expression similar to your first query before compiling. As far as what is preferred, use whatever seems more readable to you and your team.

Categories

Resources