I'm trying to implement an IEqualityComparer for my object that basically detects if an object is older that another one. The following simpler example will synthesises what i'm trying to accomplish:
class Program
{
static void Main(string[] args)
{
var authorsList = new List<Author>()
{
new Author{ Firstname = "Bob", Lastname = "Smith", Age=11 },
new Author{ Firstname = "Bob", Lastname = "Smith", Age=20 },
new Author{ Firstname = "Bob", Lastname = "Smith", Age=12 },
new Author{ Firstname = "Bob", Lastname = "Smith", Age=14 },
new Author{ Firstname = "Bob", Lastname = "Smith", Age=12 },
new Author{ Firstname = "Fred", Lastname = "Smith", Age=12 },
new Author{ Firstname = "Trevor", Lastname = "Smith", Age=15 },
new Author{ Firstname = "Brian", Lastname = "Smith", Age=11 },
new Author{ Firstname = "Billy", Lastname = "Smith", Age=11 },
};
var authorsListExcept = new List<Author>()
{
new Author{ Firstname = "Bob", Lastname = "Smith", Age=12 },
new Author{ Firstname = "Fred", Lastname = "Smith", Age=12 },
};
var authorsList2 = authorsList
.Except(authorsListExcept, new AuthorUpdatedComparer()).ToList();
}
}
class Author
{
public string Firstname { get; set; }
public string Lastname { get; set; }
public int Age { get; set; }
}
class AuthorUpdatedComparer : IEqualityComparer<Author>
{
public bool Equals(Author x, Author y)
{
return x.Age >= y.Age;
}
public int GetHashCode(Author obj)
{
//Check whether the object is null
if (Object.ReferenceEquals(obj, null)) return 0;
int FirstnameHash = (obj.Firstname ?? "").GetHashCode();
int LastnameHash = (obj.Lastname ?? "").GetHashCode();
int finalResult = FirstnameHash ^ LastnameHash;
return finalResult;
}
}
My authorsList2 result would be:
* Bob Smith with age 20
* Bom Smith with age 14
* Trevor Smith with age 15
* Brian Smith with age 11
* Billy Smith with age 11
But instead of this the Bob Smith with age 14 is not included.
When debugging I reached the conclusion that the Comparer after included Bob Smith with Age 20 start to using it has a comparer excluding then after all the Bob's younger than 20.
This is a strange behavior in my point of view it should only excluded the ones that are younger or with the same age to those included on the authorsListExcept.
I tried to read msdn documentation and what I want it should be supposed to happen:
font: http://msdn.microsoft.com/en-us/library/bb336390(v=vs.100).aspx
Anyone can help me?
Thanks,
Hugo Salgado
The following LINQ query provides the result you expect:
var result =
authorsList.GroupBy(x => Tuple.Create(x.Firstname, x.Lastname))
.SelectMany(g => g.Where(x => authorsListExcept.All(e => e.Firstname != x.Firstname || e.Lastname != x.Lastname || e.Age < x.Age)));
The following query also produces this result. It should perform better:
var result =
authorsList.GroupBy(x => Tuple.Create(x.Firstname, x.Lastname))
.GroupJoin(authorsListExcept, x => x.Key,
x => Tuple.Create(x.Firstname, x.Lastname),
(a, e) => a.Where(x => x.Age > e.Select(z => z.Age)
.DefaultIfEmpty(0)
.Max()))
.SelectMany(x => x)
And a third option (the same as the previous but in query syntax):
var result =
(from a in authorsList
group a by Tuple.Create(a.Firstname, a.Lastname) into g
join e in authorsListExcept on g.Key equals Tuple.Create(e.Firstname, e.Lastname) into er
from age in er.Select(x => x.Age).DefaultIfEmpty()
select g.Where(x => x.Age > age)).SelectMany(x => x);
The interface IEqualityComparer<T> is there to check for equality. It has nothing to do with any ordering. As such, you can't use it in the way you try.
In general: An implementation of this interface should always use the exact same set of properties in both the GetHashCode implementation and the Equals method.
If i have understood what you want try this.
class Program
{
static void Main(string[] args)
{
var authorsList = new List<Author>()
{
new Author{ Firstname = "Bob", Lastname = "Smith", Age=11 },
new Author{ Firstname = "Bob", Lastname = "Smith", Age=20 },
new Author{ Firstname = "Bob", Lastname = "Smith", Age=12 },
new Author{ Firstname = "Bob", Lastname = "Smith", Age=14 },
new Author{ Firstname = "Bob", Lastname = "Smith", Age=12 },
new Author{ Firstname = "Fred", Lastname = "Smith", Age=12 },
new Author{ Firstname = "Trevor", Lastname = "Smith", Age=15 },
new Author{ Firstname = "Brian", Lastname = "Smith", Age=11 },
new Author{ Firstname = "Billy", Lastname = "Smith", Age=11 },
};
var authorsListExcept = new List<Author>()
{
new Author{ Firstname = "Bob", Lastname = "Smith", Age=12 },
new Author{ Firstname = "Fred", Lastname = "Smith", Age=12 },
};
var authorsList2 = authorsList.Where(x => !authorsListExcept.Any(y => y.Firstname == x.Firstname && y.Lastname == x.Lastname && x.Age <= y.Age));
}
}
public class Author
{
public string Firstname { get; set; }
public string Lastname { get; set; }
public int Age { get; set; }
}
Related
I have the following objects:
var authors1 = new List<Author>() {
new Author{ FirstName = "William", LastName = "Smith" },
new Author{ FirstName = "Fred", LastName = "Jones" }
};
var authors2 = new List<Author>() {
new Author{ FirstName = "Brian", LastName = "Brains" },
new Author{ FirstName = "Billy", LastName = "TheKid" }
};
var books = new List<Book>() {
new Book{ Title = "JAVA", Description = "Description Java", Authors = authors1 },
new Book{ Title = "PHP", Description = "Description PHP", Authors = authors2 },
};
I want to create a subquery filtering by author. I know that I can do something like this:
IEnumerable<Book> list = books.Where(x => x.Authors.Where(j => j.FirstName == "William").Any());
But I would like to use the authors property as string var.
var entity = "Authors"
IEnumerable<Book> list = books.Where(x => x[entity].Where(j => j.FirstName == "William").Any());
This is not working.
I don't understand why you want to do this... But here is one way you could reach what you want with reflection:
public bool HasAuthorWithName(Book book, string authName)
{
// Retrieve list of class' properties
var p = book.GetType().GetProperties();
// Get list of authors prop by name
var entity = "Authors";
var lstAuthors = p.First(x => x.Name == entity).GetValue(book, null) as List<Author>;
// Filter by author's name
if (lstAuthors != null && lstAuthors.Any())
{
return lstAuthors.Any(j => j.FirstName == authName);
}
return false;
}
Now you can call this method:
IEnumerable<Book> list = books.Where(x => HasAuthorWithName(x, "William"));
Have a collection
List<<KeyValuePair<string, Person>>
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int MealType { get; set; }
}
patientEffort.Add("1", new Person() { FirstName = "Raja", LastName = "Ram", MealType = 2 });
patientEffort.Add("2", new Person() { FirstName = "Vijay", LastName = "Anthony", MealType = 1 });
patientEffort.Add("2", new Person() { FirstName = "Vijay", LastName = "Anthony", MealType = 2 });
patientEffort.Add("2", new Person() { FirstName = "Vijay", LastName = "Anthony", MealType = 3 });
patientEffort.Add("3", new Person() { FirstName = "Dr", LastName = "APJ", MealType = 1 });
patientEffort.Add("3", new Person() { FirstName = "Dr", LastName = "APJ", MealType = 2 });
patientEffort.Add("3", new Person() { FirstName = "Dr", LastName = "APJ", MealType = 3 });
patientEffort.Add("3", new Person() { FirstName = "Dr", LastName = "APJ", MealType = 4 });
List<int> _listMealType = new List<int>();
If _listMealType= [2] passed then Result will be
{Key: "1", FirstName = "Raja", LastName = "Ram"}
{Key: "2", FirstName = "Vijay", LastName = "Anthony"}
{Key: "3", FirstName = "Dr", LastName = "APJ"}
If _listMealType= [1,2,3] passed then Result will be
{Key: 2, FirstName = "Vijay", LastName = "Anthony"}
{Key: 3, FirstName = "Dr", LastName = "APJ"}
If _listMealType= [1,2,3,4] passed then Result will be
{Key: "3", FirstName = "Dr", LastName = "APJ"} only
Key may be string or int that doesn't matter. May I have linq query for this scenario. I have used All method is linq but not worked.
var query = patientEffort.Where(d => _listMealType.All(x => x == d.Value.MealType)).Select(d => d.Key);
Could you please help me in solving the query issue as soon as possible.
I hope it helps:
var patients = patientEffort.GroupBy(x => x.Value.FirstName);
var result = (from patient in patients let res = patient.Select(note => note.Value.MealType).ToList() where _listMealType.Intersect(res).Count() == _listMealType.Count select patient.First()).ToList();
Here is variant without linq using:
var patients = patientEffort.GroupBy(x => x.Value.FirstName); // group patients by name
foreach (var patient in patients)
{
var res = new List<int>();
foreach (var note in patient) // collect all meal types of current patient
res.Add(note.Value.MealType);
if (_listMealType.Intersect(res).Count() == _listMealType.Count) // if intersection count equal to source meal list - it's our patient.
result.Add(patient.First()); // add information about patient. because we need only name - we can use first record in list.
}
How to print out all the persons and their pets, using Linq. I only want to print out persons who have pets.
Prefer result be like:
Kate Bed:
Rex
Sally
My not working solution is here:
class Program
{
static void Main(string[] args)
{
result();
}
static void result() {
var list = StaticGenator.getPersons().Where(x => x.Pets != null);
foreach (var person in list)
{
Console.WriteLine(person.Firstname + " " + person.Lastname + ":");
foreach(var pet in list){
Console.WriteLine(" " + pet.Pets);
}
}
}
What i get is:
Kate Bed:
system.collection.generic.list'1[MainLibrary.Pet]
system.collection.generic.list'1[MainLibrary.Pet]
Here is the code to understand what I am asking:
Data is held here:
public static class StaticGenator
{
public static List<Person> getPersons()
{
List<Person> persons = new List<Person>();
persons.Add(new Person() { Firstname = "Sam", Lastname = "Car", BirthDate = new DateTime(2001, 01, 01), PersonId = 1, Sex = Sex.Man });
persons.Add(new Person() { Firstname = "Kate", Lastname = "Bed", BirthDate = new DateTime(1995, 11, 11), PersonId = 2, Sex = Sex.Woman, Pets = new List<Pet>() { new Pet { Firstname = "Rex", BirthDate = new DateTime(2007, 1, 1), Sex = Sex.Man, PetId = 1 }, new Pet { Firstname = "Sally", BirthDate = new DateTime(2004, 2, 1), Sex = Sex.Woman, PetId = 2 } } });
return persons;
}
}
Person Class:
public class Person
{
public int PersonId { get; set; }
public string Firstname { get; set; }
public string Lastname { get; set; }
public DateTime BirthDate { get; set; }
public Sex Sex{ get; set; }
public int Age {
get
{
var age= DateTime.Now.Year - BirthDate.Year;
if (DateTime.Now.Day >= BirthDate.Day && DateTime.Now.Month >= BirthDate.Month)
return age;
else
return age- 1;
}
}
public List<Pet> Pets { get; set; }
}
Pet Class:
public class Pet
{
public int PetId { get; set; }
public String Firstname { get; set; }
public Sex Sex { get; set; }
public DateTime BirthDate { get; set; }
public int Age { get; set; }
}
Sex enum:
public enum Sex{
Man,
Woman
}
foreach (var person in list)
{
Console.WriteLine(person.Firstname + " " + person.Lastname + ":");
foreach(var pet in person.Pets) // iterate over Pets of person
{
Console.WriteLine(" " + pet.Firstname); // write pet's name
}
}
Keep in mind - you can have problem if somebody will add null pet to pets collection or if there is empty pets list. So, probably correct query to get persons with pets is:
var peopleWithPets = from p in StaticGenator.getPersons()
where p.Pets != null &&
p.Pets.Any() &&
p.Pets.All(x => x != null)
select p;
Also use string formatting:
foreach (var person in peopleWithPets)
{
Console.WriteLine("{0} {1}:", person.Firstname, person.Lastname);
foreach(var pet in person.Pets)
Console.WriteLine("\t{0}", pet.Firstname);
}
Also I suggest you to follow Capitalization Styles recommended by MicroSoft.
foreach(var pet in list)
{
Console.WriteLine(" " + pet.Pets);
}
Should be:
foreach(var pet in person.Pets)
{
Console.WriteLine(" " + pet.FirstName);
}
You have to iterate over the pet collection of the current user that is being iterated.
Suppose you have some list (you mentioned above) with your datastructure
List<Person> persons = new List<Person>();
persons.Add(new Person() { Firstname = "Sam", Lastname = "Car", BirthDate = new DateTime(2001, 01, 01), PersonId = 1, Sex = Sex.Man });
persons.Add(new Person() { Firstname = "Kate", Lastname = "Bed", BirthDate = new DateTime(1995, 11, 11), PersonId = 2, Sex = Sex.Woman, Pets = new List<Pet>() { new Pet { Firstname = "Rex", BirthDate = new DateTime(2007, 1, 1), Sex = Sex.Man, PetId = 1 }, new Pet { Firstname = "Sally", BirthDate = new DateTime(2004, 2, 1), Sex = Sex.Woman, PetId = 2 } } });
You can very easily filter as well as write to console with these 4 lines of code
persons.Where(p => p.Pets != null && p.Pets.Any()).ToList().ForEach(p =>
{
Console.WriteLine(p.Firstname + " " + p.Lastname + "\n");
p.Pets.ForEach(pt => Console.WriteLine(pt.Firstname));
});
var output = String.Join("\n", persons.Select(person => $"{person.Firstname} {person.Lastname}"));
Output:
Sam Car
Kate Bed
I have a school project with a one to many relationship (Contact can have many Addresses). But I don't know how to seed it correctly.
In my Data models Contact has a virtual ICollection<Address> Addresses and the address object has the foreign key of ContactId.
So here is my seed data (code first) And i need to make it so when i type in the contacts last name in a search bar it will pull up all the info on that contact (address Info).
So how do i associate the info together in my seed data so when you search it pulls up what it is supposed to?
namespace Success.Data.Migrations
{
public class Seeder
{
public static void Seed(SuccessContext context,
bool seedContacts = true,
bool seedAddresses = true)
{
if (seedContacts) SeedContacts(context);
if (seedAddresses) SeedAddresses(context);
}
private static void SeedContacts(SuccessContext context)
{
context.Contacts.AddOrUpdate(l => l.LastName,
new Contact() { FullName = "Darth Vader", FirstName = "Darth", LastName = "Vader", },
new Contact() { FullName = "Luke Skywalker", FirstName = "Luke", LastName = "Skywalker", },
new Contact() { FullName = "Tony Stark", FirstName = "Tony", LastName = "Stark", },
new Contact() { FullName = "Ricky Bobby", FirstName = "Ricky", LastName = "Bobby", },
new Contact() { FullName = "Trix Rabbit", FirstName = "Trix", LastName = "Rabbit", });
context.SaveChanges();
}
private static void SeedAddresses(SuccessContext context)
{
context.Addresses.AddOrUpdate(h => h.HomeAddress,
new Address() { HomeAddress = "1300 DeathStar", BusinessAddress = "444 Imperial Fleet", PoBox = "PO Box 1335", ContactId = 1, },
new Address() { HomeAddress = "1997 Endor", BusinessAddress = "448 Rebel Fleet", PoBox = "PO Box 1339", ContactId = 2, },
new Address() { HomeAddress = "1224 Malibu Point", BusinessAddress = "657 Stark Industries", PoBox = "PO Box 1337", ContactId = 3, },
new Address() { HomeAddress = "9978 Fast LN.", BusinessAddress = "532 NASCAR Race Track", PoBox = "PO Box 1333", ContactId = 4, },
new Address() { HomeAddress = "9864 Cerial Box LN", BusinessAddress = "8432 Kellog Dr.", PoBox = "PO Box 1338", ContactId = 5, });
context.SaveChanges();
}
}
}
You could have another method that seeds both the contacts and address. You will need an extra if/else switch
if (seedContacts && seedAddresses)
{
SeedContactsAndAddress(context);
}
else
{
if (seedContacts) SeedContacts(context);
if (seedAddresses) SeedAddresses(context);
}
And the SeedContactsAndAddress Method would look like this:
private static void SeedContactsAndAddress(StoreContext context)
{
// Each Address, which I believe is a collection in this case, but there is only
// one, will have to be created and added to each contact.
var addressesForDarthVader = new List<Address>
{
new Address { HomeAddress = "1300 DeathStar", BusinessAddress = "444 Imperial Fleet", PoBox = "PO Box 1335" }
// Add more addresses for Darth Vader if you need to
};
// Rinse and repeat for the other contacts;
context.Contacts.AddOrUpdate(l => l.LastName,
new Contact() { FullName = "Darth Vader", FirstName = "Darth", LastName = "Vader", Addresses = addressesForDarthVader },
new Contact() { FullName = "Luke Skywalker", FirstName = "Luke", LastName = "Skywalker", },
new Contact() { FullName = "Tony Stark", FirstName = "Tony", LastName = "Stark", },
new Contact() { FullName = "Ricky Bobby", FirstName = "Ricky", LastName = "Bobby", },
new Contact() { FullName = "Trix Rabbit", FirstName = "Trix", LastName = "Rabbit", });
context.SaveChanges();
}
How can I modify the following code:
var people = new[] {
new { name = "John", surname = "Smith" },
new { name = "John", surname = "Doe" },
};
To not use the var keyword (so I can initialise the variable in an object initialiser) and still be able to access the elements like this?:
System.Console.WriteLine(people[0].surname); //John
System.Console.WriteLine(people[1].surname); //Doe
Define a model:
public class Person
{
public string Name { get; set; }
public string Surname { get; set; }
}
and then have a collection of this model:
List<Person> people = new List<Person>();
people.Add(new Person { Name = "John", Surname = "Smith" });
people.Add(new Person { Name = "John", Surname = "Doe" });
or:
var people = new List<Person>
{
new Person { Name = "John", Surname = "Smith" },
new Person { Name = "John", Surname = "Doe" }
};
and then you can still:
System.Console.WriteLine(people[0].Surname); //John
System.Console.WriteLine(people[1].Surname); //Doe
You cannot; you will have to define a proper class for these objects or reuse one (e.g. Tuple).
Technically the one-word change from var to dynamic will also do the trick, but of course this changes the essence of the member dramatically so it's not equivalent by any stretch of the imagination.
First you'll need to create a named type for that data:
public class Person
{
public string Name { get; set; }
public string Surname { get; set; }
}
Then use that type when creating the array:
People[] people;
//...
people = new People[]{
new Person{ Name = "John", Surname = "Smith" },
new Person{ Name = "John", Surname = "Doe" },
};
You could use 'dynamic'
dynamic people = new[] {
new { name = "John", surname = "Smith" },
new { name = "John", surname = "Doe" },
};
And then call it
Console.WriteLine(people[0].name);
Note - Works on framework 4.0 onwards, also comes with the caveats mentioned already.