I have a list of custom class called List<Notifications>.
The class is below:
public class Notification
{
public enum Type {
Promotion,
Other
}
public string ID { get; set; }
public string Headline { get; set; }
public string Detail { get; set; }
public Type NotificationType { get; set; }
}
Before adding an instance of the Notification class to my custom list, I want to check if it is already in the list.
What is the best way to achieve this?
You can use 1.) Contains, but then you have to override Equals (+ GethashCode).
bool contains = list.Contains(someNotificationInstance);
For example:
public class Notification
{
public enum Type {
Promotion,
Other
}
public string ID { get; set; }
public string Headline { get; set; }
public string Detail { get; set; }
public Type NotificationType { get; set; }
public override bool Equals(object obj)
{
return obj is Notification && string.Equals(ID, ((Notification)obj).ID);
}
public override int GetHashCode()
{
return ID == null ? 0 : ID.GetHashCode();
}
}
2.) another option is to provide a custom IEqualityComparer<Notification> for Contains:
public class NotificationComparer : IEqualityComparer<Notification>
{
public bool Equals(Notification x, Notification y)
{
return x.ID == y.ID;
}
public int GetHashCode(Notification obj)
{
return obj.ID == null ? 0 : obj.ID.GetHashCode();
}
}
On this way you don't need to modify the original class. You can use it in this way:
bool contains = list.Contains(someInstance, new NotificationComparer());
3.) Probably the easiest approach is using Enumerable.Any:
bool contains = list.Any(n => someInstance.ID == n.ID);
4.) The most efficient approach is using a set if no duplicates are allowed in the collection anyway. Then you can use the first or second approaches for a HashSet<T>:
var set = new HashSet<Notification>(new NotificationComparer());
set.Add(instance1);
bool contains = !set.Add(instance2);
You can check it with Contains method.
if (!mylist.Select(l => l.ID).Contains(mynewid)) {
var item = new Notifcation();
item.ID = mynewid;
item..... // fill the rest
mylist.Add(item);
}
Maybe a better approch would be use of Dictionary.
Related
I've been playing with various ways of using generics and have hit a road block.
Consider the following classes:
public abstract class DataElement
{
public int Id { get; set; }
}
public class School : DataElement
{
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
}
public class Course : DataElement
{
public int Id { get; set; }
public int SchoolId { get; set; }
public string Name { get; set; } = string.Empty;
}
public class Student : DataElement
{
public int Id { get; set; }
public int CourseId { get; set; }
public string Name { get; set; } = string.Empty;
public string Phone { get; set; } = string.Empty;
public string Email { get; set; } = string.Empty;
}
Considering a hypothetical scenario where none of this data changes, I'm trying to create a DataDictionary class to house those objects in their respective Lists all within one top-level List property. This crazy idea came to me, when I was writing code to load the different data types from JSON files. I was able to write one load method that could read all three types of data using generics, and that sent me down this particular rabbit hole.
public interface IDataDictionary
{
public List<T> GetAllItemsFromList<T>();
public T GetSingleItemFromList<T>(int id);
}
public class DataDictionary : IDataDictionary
{
public List<IList> Data = new List<IList>();
// Return all objects from the list of type T
public List<T> GetAllItemsFromList<T>()
{
return Data.OfType<T>().ToList(); // This works, returning the appropriate list.
}
// Return specific object from the list of type T by Id property value
public T GetSingleItemFromList<T>(int id)
{
List<T> list = Data.OfType<List<T>>().First(); // This works, resolving to the correct list (e.g. Courses).
return list.Where(i => i.Id == id).First(); // This doesn't work. It doesn't appear to know about the Id property in this context.
}
}
GetAllItemsFromList works fine, returning the appropriate list of items
List<School> newList = GetAllItemsFromList<School>();
However, I am unable to figure out how to return a single item by Id from its respective list.
School newSchool = GetSingleItemFromList<School>(1);
It could be that I'm just trying to be too clever with this, but I can't help but think there is a way to do this, and I'm just missing it.
This doesn't work. It doesn't appear to know about the Id property in this context.
Yes, because you have not specified any constraints to the type parameter T, so it can be anything. There are multiple options:
Create an interface IHaveId and constraint T to it (or use DataElement):
public interface IHaveId
{
int Id { get; set; }
}
public interface IDataDictionary
{
public List<T> GetAllItemsFromList<T>();
public T GetSingleItemFromList<T>(int id) where T : IHaveId; // or where T : DataElement
}
public class DataDictionary : IDataDictionary
{
public T GetSingleItemFromList<T>(int id) where T : IHaveId // or where T : DataElement
{
List<T> list = Data.OfType<List<T>>().First();
return list.Where(i => i.Id == id)
.First();
}
}
add additional parameter to select the id:
public T GetSingleItemFromList<T>(int id, Func<T, int> idSelector)
{
List<T> list = Data.OfType<List<T>>().First();
return list.First(i => idSelector(i) == id);
}
use reflection - I would argue the least recommended option
The problem is that T parameter in function GetSingleItemFromList<T>(int id) could be anything. In order of this to work you should add a constraint:
GetSingleItemFromList<T>(int id) where T: DataElement
I want to practice code with DRY principle, but my method uses 2 different classes, classOneDTO and classTwoDTO.. They have different properties and I want to linked it with PRIMARYIDENTIFIER prop with both have the same..
How can I create a generic method to get the property that I want to query with Linq.
Updated: my purpose is to have a generic method that will query the PrimaryIdentifier and get the data to it whether they are using classOneDTO or classTwoDTO. Is there a way to have a single generic method to do this?
private void genericMethod<T>(List<T> workList, GridView grid, int columnNo)
{
if (workList.Any())
{
string CodeString = default;
// Want to dynamic get the properties in different class with PrimaryIDentifier property
// want to check if PrimaryIdentifier is NULL OR EMPTY
var getDataOne = workList.Cast<classOneDTO>().FirstOrDefault(x => !string.IsNullOrEmpty(x.PrimaryIdentifier));
// causing error because of the CAST if wrong type request
var getDataTwo = workList.Cast<classTwoDTO>().FirstOrDefault(x => !string.IsNullOrEmpty(x.PrimaryIdentifier));
if (getDataOne != null || getDataTwo != null)
{
CodeString = (getDataOne != null) ? getDataOne.PrimaryIdentifier : getDataTwo.PrimaryIdentifier;
}
}
}
public class classOneDTO
{
public int PatientID { get; set; }
public string PrimaryIdentifier { get; set; }
public string FirstName{ get; set; }
// so oonnn...
}
public class classTwoDTO
{
public int EntryID { get; set; }
public string PrimaryIdentifier { get; set; }
public string Location{ get; set; }
// so oonnn...
}
All that you need is to make both your classes implement the same interface, i.e. IDTO:
public interface IDTO
{
string PrimaryIdentifier { get; set; }
}
Then you can tell the compiler to accept only types that implement your new interface:
private void GenericMethod<DTO>(List<DTO> workList, GridView grid, int columnNo)
where DTO: IDTO
{
if (workList.Any())
{
string CodeString = default;
var getData = workList.FirstOrDefault(x => !string.IsNullOrEmpty(x.PrimaryIdentifier));
if (getData != null)
{
CodeString = getData?.PrimaryIdentifier;
}
}
}
(Pay attention to the 2nd row)
Additionally, I also made minor adjustments to your class and method namings based on standard .Net naming convention.
Here's the full code:
public class Client
{
private void GenericMethod<DTO>(List<DTO> workList, GridView grid, int columnNo)
where DTO: IDTO
{
if (workList.Any())
{
string CodeString = default;
var getData = workList.FirstOrDefault(x => !string.IsNullOrEmpty(x.PrimaryIdentifier));
if (getData != null)
{
CodeString = getData?.PrimaryIdentifier;
}
}
}
}
public class ClassOneDTO : IDTO
{
public int PatientID { get; set; }
public string PrimaryIdentifier { get; set; }
public string FirstName { get; set; }
// so oonnn...
}
public class ClassTwoDTO : IDTO
{
public int EntryID { get; set; }
public string PrimaryIdentifier { get; set; }
public string Location { get; set; }
// so oonnn...
}
public interface IDTO
{
string PrimaryIdentifier { get; set; }
}
EDIT: as Johnathan Barclay correctly pointed out, there's actually no need to have a generic method if you don't need some more advanced logic there that you didn't show in your example.
private void GenericMethod(IEnumerable<IDTO> workList, GridView grid, int columnNo)
{
if (workList.Any())
{
string CodeString = default;
var getData = workList.FirstOrDefault(x => !string.IsNullOrEmpty(x.PrimaryIdentifier));
if (getData != null)
{
CodeString = getData?.PrimaryIdentifier;
}
}
}
I have these two classes
enum CustomerType {
CitizenBank = 0,
Wellsfargo = 1
}
public abstarct class CustomerDto {
int customerId {
get;
set;
}
string customerName {
get;
set;
}
string CustometAddress {
get;
set;
}
int CustomerTypeId {
get;
set;
}
}
public CitizenBank: CustomerDto {}
public Wellsfargo: CustomerDto {}
Public Class CustomerEntity {
int customerId {
get;
set;
}
string customerName {
get;
set;
}
string CustometAddress {
get;
set;
}
int CustomerTypeId {
get;
set;
}
}
I wrote a class to convert from entity to DTO
public class EntityModelToModel {
///Method to convert
public CustomerDto ToDto(CustomerEntity customerEntity) {
/// returns type customerDto based on customertypeid
switch (CustomerType) {
case Wellsfargo
return New Wellsfargo()
case citizen
return new citizen() //calls method that converts from customer Entity to citizen
}
}
I have method to check if my types match
public bool CanExecute(CustomerEntity customerEntity) {
foreach(var customerType in Enum.GetValues(typeof(Enums.customerType) if (customerEntity.CustomerType == customerType)
return true
else
false
}
}
Now my calling code I have array of CustomerEntity[] which have two items of customerid for wellsfargo and citizens. I want to do this
var res = CustomerEntity[].where(x => EntityModelToModel.CanExecute(x).Select(x => EntityModelToModel.ToDto(x))
My problem is:
If my array has two items this only checks the first items and returns.
What I want is it should check for two items and return them.
I think that you should change your CanExecute method like this:
public static class EntityModelToModel
{
// ...
public static bool CanExecute(CustomerEntity customerEntity)
{
foreach (var customerType in Enum.GetValues(typeof(CustomerType)))
{
if (customerEntity.CustomerTypeId == (int)customerType)
{
return true;
}
}
return false;
}
}
Because your method break execution flow after first check.
Trying to check that my ObservableCollection doesn't have an object matching the one i'm trying to add.
For example:
public class LoggedUsers : NotifyUIBase
{
public ObservableCollection<Logged> UserList { get; private set; }
public LoggedUsers()
{
UserList = new ObservableCollection<Logged>();
}
public void Add(Logged user)
{
if (UserList.Where(x => x == user))
{
UserList.Add(user);
}
}
}
So if the ObservableCollection has got a Logged object with the same property values as the Add(Logged user) then I want it to not add the user.
Edit 1:
public class LoggedUsers : NotifyUIBase
{
public ObservableCollection<Logged> UserList { get; private set; }
public LoggedUsers()
{
UserList = new ObservableCollection<Logged>();
}
public void Add(Logged user)
{
if (!UserList.Any(x => x == user))
{
UserList.Add(user);
////Check the List for users
UpdateView();
}
else
{
MessageBox.Show("Already exists!");
}
}
Edit 2:
Changed my Logged class to the following:
namespace PhotoManagement
{
public class Logged : Common.NotifyUIBase
{
public string ClearPassword { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public long UID { get; set; }
public string Email { get; set; }
//Display basic statistics
public int ThisDayImageCount { get; set; }
public int ThisDaySaleCount { get; set; }
public Logged()
{
//Update the stats when instigated
UpdateStats();
}
//Update the stats
public void UpdateStats()
{
}
public bool Equals(Logged other)
{
if (other == null) return false;
return (this.UID.Equals(other.UID));
}
}
}
So this should work? However it still says Already Exists when I have checked the instances and they are different.
public class LoggedUsers : NotifyUIBase
{
public ObservableCollection<Logged> UserList { get; private set; }
public LoggedUsers()
{
UserList = new ObservableCollection<Logged>();
}
public void Add(Logged user)
{
if (!UserList.Any(x => x.Equals(user)))
{
UserList.Add(user);
UpdateView();
}
else
{
MessageBox.Show("Already exists!");
}
}
The best solution is to implement IEquatable as said #vadim-martynov.
Another fast but no so clean solution is to search by explicitly comparing values:
public void Add(Logged user)
{
if (!UserList.Any(x => x.Id == user.Id && x.UserName == user.UserName))
{
UserList.Add(user);
////Check the List for users
UpdateView();
}
else
{
MessageBox.Show("Already exists!");
}
}
Edit 1:
Think about your Logged class. The key here is to implement the IEquatable interface who implement Equals method. There should you implement the explicitly comparing you will apply then we call Equals on this class.
public class Logged: IEquatable<Logged>
{
public int Id { get; set; }
public string UserName { get; set; }
public string OtherProperty { get; set; }
public bool Equals(Logged other)
{
return this.Id == other.Id && this.UserName == other.UserName;
}
}
After you Add method will look like:
public void Add(Logged user)
{
if (!UserList.Any(x => x.Equals(user)))
{
UserList.Add(user);
////Check the List for users
UpdateView();
}
else
{
MessageBox.Show("Already exists!");
}
}
Edit 2:
Ok, then we must try a planb. Try implementing IComparable and try using with this. IEquatable should work but if not, this could work too.
public class Logged: IEquatable<Logged>, IComparable<Logged>
{
public int Id { get; set; }
public string UserName { get; set; }
public string OtherProperty { get; set; }
public bool Equals(Logged other)
{
return this.Id == other.Id && this.UserName == other.UserName;
}
public int CompareTo(Logged other)
{
return this.Id.CompareTo(other.Id);
}
}
IComparable returns int. So the usage and integer result are:
var result = x.CompareTo(user)
// Comparing 'some text' with '123': 1
// Comparing 'some text' with 'some text': 0
// Comparing 'some text' with 'Some Text': -1
Try to specify the attribute in the query to find match, ex. Username or User ID.
public void Add(Logged user)
{
if (!UserList.Any(u => u.Username == user.Username))
{
UserList.Add(user);
}
}
By default your object compared by reference via == operator. It's common feature of reference types in c#.
You can override this operator or override Equals method.
The new implementation of Equals should not throw exceptions. It is
recommended that any class that overrides Equals also override
System.Object.GetHashCode. It is also recommended that in addition to
implementing Equals(object), any class also implement Equals(type) for
their own type, to enhance performance.
Also, you can implement IEquatable<T> for your Logged class.
To avoid having different results depending on the way you check the
equality of the objects, you should override the Object.Equals method
in your class. This ensures that whenever a class doesn't use
the IEquatable.Equals method, still the same checks will be performed.
Finally you can compare some properties of 2 instances inside Where method:
if(!UserList.Any(u => u.Login == user.Login))
{
UserList.Add(user);
}
I want to fetch the details of manager from manager class if at run time I give kinid of employee from the employee class. How can I do this using Equals or Hashcode?
public class employee
{
public string empname { get; set;}
public string location { get; set; }
public int kinid { get; set; }
public double magkin { get; set; }
}
public class manager
{
public string magname { get; set; }
public double magkin { get; set; }
}
Dictionary<employee, manager> relation = new Dictionary<employee, manager>();
I haven't used C# in a long time, but Something like this should work:
kinidFind is the kinid you want to search for.
manager findManager(int kinidFind) {
foreach( KeyValuePair<employee, manager> i in relation) {
if (i.Key.kinid==kinidFind) {
return i.Value;
}
}
}
Assuming that kinid is a unique identifier (you don't care about other fields of employee in identifying), then on employee class you could do the following:
override int GetHashCode()
{
return kinid;
}
override bool Equals(Object obj)
{
if (obj == null) return false;
emploee emp = obj as employee;
if ((System.Object)emp == null) return false;
return (kinid == emp.kinid);
}
However, this is not a good general solution because what if later on you want to find the employee by other fields?
Consider changing that dictionary to:
Dictionary<int,manager> where the int is the kinid of the employee then it's self explanatory.
or since the Dictionary has been loaded you can Enumerate it this way as well it's just a question of feel or taste at this point from Brads answer
foreach (var pair in relation)
{
if (pair.key == kinidFind )
{
return relation[pair.key];
}
}