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
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();
}
}
}
Something like this:
Produces the following output -
-saige-
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();
}
}
}
Produces the following output -
-saige-
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.
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:
Now produces the following output -
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();
}
}
}
Now produces the following output -
ASKER
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.
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.
ASKER
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-
Is that the gist of it?
-saige-
ASKER
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.
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
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
I modified the code to add UK to the second list and also added an ordering on the first list as well:
Now produces the following output -
-saige-
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();
}
}
}
Now produces the following output -
-saige-
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
One other edit in case you don't require "USA" or "UK" in the second list but always require them in the finished list:
Produces the following output -
-saige-
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();
}
}
}
Produces the following output -
-saige-
SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
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.
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.
The code I posted is only 7 lines more then your original post.
ASKER
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.
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