Link to home
Start Free TrialLog in
Avatar of John Bolter
John Bolter

asked on

Really complicated no for or foreach loop LINQ question

Hi, I need help with some LINQ. I can do this myself with for or foreach loops but I want to be better skilled in LINQ so want to see how it is done only with LINQ.
All help really appreciated.
Thank you
John
using System;
using System.Collections.Generic;

namespace ConsoleApplication30
{
    class Country
    {
        public String Name { get; set; }
        public String Currency { get; set; }
    }

    class Program
    {
        public void Run()
        {
            List<Country> topCountries = new List<Country> { 
                                                             new Country{ Name =" USA", Currency = "$" }, 
                                                             new Country{ Name = "UK" },
                                                             new Country{ Name = "China", Currency = "Y" }
                                                           };

            List<Country> otherCountries = new List<Country> { new Country { Name = "Germany" }, 
                                                               new Country { Name = "Austria" }, 
                                                               new Country { Name = "Switzerland" }, 
                                                               new Country { Name = "Austria" } ,           //second copy
                                                               new Country { Name = "France" } ,
                                                               new Country { Name = "Spain" } ,
                                                               new Country { Name = "Italy" } ,
                                                               new Country { Name = "Portugal" } ,
                                                               new Country { Name = "Belgium" } ,
                                                               new Country{Name = "USA", Currency="USD" }   //USA already exists, with different 
                                                                       //currency, but I don't care which one I get back
                                                              };
            //What I want, without using foreach or for loops, is a solely LINQ solution that
            //will create new list that contains the following in the following order


            //the bit I can't write but I think it may use LINQ extension methods .Except and .Union and .Sort and .Distinct
            List<Country> countryYouLiveIn = null; // <--- I CAN'T WRITE THIS BIT

            foreach(var c in countryYouLiveIn)
                Console.WriteLine(c.Name);
            // Would print out the following in this order
            //
            //      UK
            //      USA
            //      Austria
            //      Belguim
            //      Italy
            //      France
            //      Germany
            //      Portugal
            //      Spain
            //      Switzerland
            //
            //So topcountries if they exist are ordered alphabetically but first
            //and otherCountries are added afterwards, also alphabetised
        }

        static void Main(string[] args)
        {
            Program program = new Program();
            program.Run();
        }
    }
}

Open in new window

Avatar of Fernando Soto
Fernando Soto
Flag of United States of America image

Hi John;

I don't see a pattern in which you use the two list, topCountries and  otherCountries, to create the new list countryYouLiveIn. Can you explain how to combine the two lists to get the new third list in words. Thanks
Something like this:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace EE_Q28576422
{
	class Country
	{
		public String Name { get; set; }
		public String Currency { get; set; }
	}


	class Program
	{
		private static List<Country> topCountries = new List<Country> 
		{
			new Country{ Name =" USA", Currency = "$" }, 
			new Country{ Name = "UK" },
			new Country{ Name = "China", Currency = "Y" }
		};

		private static List<Country> otherCountries = new List<Country> 
		{ 
			new Country { Name = "Germany" }, 
			new Country { Name = "Austria" }, 
			new Country { Name = "Switzerland" }, 
			new Country { Name = "Austria" } ,           //second copy
			new Country { Name = "France" } ,
			new Country { Name = "Spain" } ,
			new Country { Name = "Italy" } ,
			new Country { Name = "Portugal" } ,
			new Country { Name = "Belgium" } ,
			new Country {Name = "USA", Currency="USD" }
		};

		static void Main(string[] args)
		{
			var countryYouLiveIn = (from country in topCountries.Concat(otherCountries)
					    group country by country.Name.Trim() into g
					    where g != null
					    select new Country() { Name = g.First().Name, Currency = g.First().Currency });
			foreach (var country in countryYouLiveIn)
				Console.WriteLine(country.Name);
			Console.ReadLine();
		}
	}
}

Open in new window


Produces the following output -User generated image
-saige-
Avatar of John Bolter
John Bolter

ASKER

Hi Fernando
It is going to be a dropdown list to enable people to select the country they live in.
I want it alphabetically for all countries, but I want topCountries to appear at the top, regardless.
It's like when you go to Amazon to buy something. They list all the countries, but the USA and UK are always at the top.
Well since you want ordering in the second list, we could do this:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace EE_Q28576422
{
	class Country
	{
		public String Name { get; set; }
		public String Currency { get; set; }
	}


	class Program
	{
		private static List<Country> topCountries = new List<Country> 
		{
			new Country{ Name =" USA", Currency = "$" }, 
			new Country{ Name = "UK" },
			new Country{ Name = "China", Currency = "Y" }
		};

		private static List<Country> otherCountries = new List<Country> 
		{ 
			new Country { Name = "Germany" }, 
			new Country { Name = "Austria" }, 
			new Country { Name = "Switzerland" }, 
			new Country { Name = "Austria" } ,           //second copy
			new Country { Name = "France" } ,
			new Country { Name = "Spain" } ,
			new Country { Name = "Italy" } ,
			new Country { Name = "Portugal" } ,
			new Country { Name = "Belgium" } ,
			new Country {Name = "USA", Currency="USD" }
		};

		static void Main(string[] args)
		{
			var countryYouLiveIn = (from country in topCountries.Concat(otherCountries.OrderBy(country => country.Name))
					    group country by country.Name.Trim() into g
					    where g != null
					    select g.FirstOrDefault());
			foreach (var country in countryYouLiveIn)
				Console.WriteLine(country.Name);
			Console.ReadLine();
		}
	}
}

Open in new window


Now produces the following output -User generated image
Hi Saige, almost.

Only countries that are in the otherCountries list should be in List<String>. So China shouldn't be there because it isn't. The topCountries is only used for ordering of the first countries, so it would be (not China), then UK, then USA, then all the other countries alphabetised.
Saige, I just hit submit and then your second reply came through. It is absolutely what I want ordered now, except that China is in there. As China isn't in the otherCountries list, it shouldn't appear.
Ok so that I understand.  The top countries should only appear if they are in the other countries list.  The other countries list is then ordered (by Name) and the top countries are subtracted from the other countries list.

Is that the gist of it?

-saige-
Absolutely, that is it!

When someone buys something on a website, I want to give them the option in a dropdown of entering any country, but Afghanistan, Albania, Algeria etc shouldn't be at the top, the UK and USA should be, alphabetised, and then all the other countries that are in the topCountries list should listed underneath, alphabetised too.
SOLUTION
Avatar of it_saige
it_saige
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
I modified the code to add UK to the second list and also added an ordering on the first list as well:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace EE_Q28576422
{
	class Country
	{
		public String Name { get; set; }
		public String Currency { get; set; }

		#region Overriden Methods
		public override bool Equals(object obj)
		{
			if (obj == null || (obj.GetType() != GetType()))
				return false;

			return this == (obj as Country);
		}

		public bool Equals(Country country)
		{
			if (object.ReferenceEquals(country, null))
				return false;

			return this == country;
		}

		public override int GetHashCode()
		{
			return Name != null ? Name.GetHashCode() : 0;
		}
		#endregion

		#region Operator Overloads
		public static bool operator ==(Country lhs, Country rhs)
		{
			if (object.ReferenceEquals(lhs, rhs))
				return true;

			if (object.ReferenceEquals(lhs, null) || object.ReferenceEquals(rhs, null))
				return false;

			return (lhs.Name != null && rhs.Name != null ? string.Compare(lhs.Name.Trim(), rhs.Name.Trim(), StringComparison.InvariantCultureIgnoreCase) == 0 : false);
		}

		public static bool operator !=(Country lhs, Country rhs)
		{
			return !(lhs == rhs);
		}
		#endregion
	}


	class Program
	{
		private static List<Country> topCountries = new List<Country> 
		{
			new Country{ Name ="USA", Currency = "$" }, 
			new Country{ Name = "UK" },
			new Country{ Name = "China", Currency = "Y" }
		};

		private static List<Country> otherCountries = new List<Country> 
		{ 
			new Country { Name = "Germany" }, 
			new Country { Name = "Austria" }, 
			new Country { Name = "Switzerland" }, 
			new Country { Name = "Austria" } ,           //second copy
			new Country { Name = "France" } ,
			new Country { Name = "Spain" } ,
			new Country { Name = "UK" } ,
			new Country { Name = "Italy" } ,
			new Country { Name = "Portugal" } ,
			new Country { Name = "Belgium" } ,
			new Country {Name = "USA", Currency="USD" }
		};

		static void Main(string[] args)
		{
			var countryYouLiveIn = (from country in topCountries.OrderBy(country => country.Name).Union(otherCountries.OrderBy(country => country.Name))
					    where otherCountries.Contains(country)
					    group country by country.Name.Trim() into g
					    where g != null
					    select g.FirstOrDefault());
			foreach (var country in countryYouLiveIn)
				Console.WriteLine(country.Name);
			Console.ReadLine();
		}
	}
}

Open in new window


Now produces the following output -User generated image
-saige-
ASKER CERTIFIED SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
One other edit in case you don't require "USA" or "UK" in the second list but always require them in the finished list:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace EE_Q28576422
{
	class Country
	{
		public String Name { get; set; }
		public String Currency { get; set; }

		#region Overriden Methods
		public override bool Equals(object obj)
		{
			if (obj == null || (obj.GetType() != GetType()))
				return false;

			return this == (obj as Country);
		}

		public bool Equals(Country country)
		{
			if (object.ReferenceEquals(country, null))
				return false;

			return this == country;
		}

		public override int GetHashCode()
		{
			return Name != null ? Name.GetHashCode() : 0;
		}
		#endregion

		#region Operator Overloads
		public static bool operator ==(Country lhs, Country rhs)
		{
			if (object.ReferenceEquals(lhs, rhs))
				return true;

			if (object.ReferenceEquals(lhs, null) || object.ReferenceEquals(rhs, null))
				return false;

			return (lhs.Name != null && rhs.Name != null ? string.Compare(lhs.Name.Trim(), rhs.Name.Trim(), StringComparison.InvariantCultureIgnoreCase) == 0 : false);
		}

		public static bool operator !=(Country lhs, Country rhs)
		{
			return !(lhs == rhs);
		}
		#endregion
	}


	class Program
	{
		private static List<Country> topCountries = new List<Country> 
		{
			new Country{ Name =" USA", Currency = "$" }, 
			new Country{ Name = "UK" },
			new Country{ Name = "China", Currency = "Y" }
		};

		private static List<Country> otherCountries = new List<Country> 
		{ 
			new Country { Name = "Germany" }, 
			new Country { Name = "Austria" }, 
			new Country { Name = "Switzerland" }, 
			new Country { Name = "Austria" } ,           //second copy
			new Country { Name = "France" } ,
			new Country { Name = "Spain" } ,
			new Country { Name = "Italy" } ,
			new Country { Name = "Portugal" } ,
			new Country { Name = "Belgium" } ,
			new Country {Name = "USA", Currency="USD" }
		};

		static void Main(string[] args)
		{
			var countryYouLiveIn = (from country in topCountries.OrderBy(country => country.Name).Union(otherCountries.OrderBy(country => country.Name))
					    where country.Name.Trim().Equals("USA") || country.Name.Trim().Equals("UK") || otherCountries.Contains(country)
					    group country by country.Name.Trim() into g
					    where g != null
					    select g.FirstOrDefault());
			foreach (var country in countryYouLiveIn)
				Console.WriteLine(country.Name);
			Console.ReadLine();
		}
	}
}

Open in new window


Produces the following output -User generated image
-saige-
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
I understand your code Fernando and Saige but Mike's is the most simple under 10 lines long also looking like SQL. I just can't understand it though.
All of your codes work, Fernando, Saige, and Mike, thank you.
Hi John;

The code I posted is only 7 lines more then your original post.
Yes, it is Fernando, 7 lines verses 10 lines.  I understand what your C# is doing too just by intuitively looking at your code, but Mike's all LINQ code does look really nice and concise doesn't it? It even looks simple, but I don't think it is. I couldn't have written this all LINQ example even though it is what I wanted and I still don't understand it.