I came to this site searching for object comparison in Dictionary, and i came to know that overriding GetHashCode and Equals are a must for doing object comparison in C#.
Here is a piece of code that i have been trying to solve out, using FOREACH iteration Method. But my Boss says to do the same without using any iteration(maybe by using containskey or containsvalue method), due to performance issues. Any help is highly welcome..
public class employee
{
public string empname { get; set; }
public string location { get; set; }
public double kinid { get; set; }
public double managerKin { get; set; }
public override bool Equals(object obj)
{
return base.Equals(obj);
}
public override int GetHashCode()
{
return base.GetHashCode();
}
}
public class manager
{
public string managername { get; set; }
public double kinid { get; set; }
public override int GetHashCode()
{
return 17 * managername.GetHashCode() + kinid.GetHashCode();
}
}
public class program
{
public static void Main()
{
employee emp = new employee();
employee emp2 = new employee();
manager mng = new manager();
manager mng2 = new manager();
emp.empname = "Deepak";
emp.location = "Pune";
emp.kinid = 36885;
emp.managerKin = 007;
emp2.empname = "Astha";
emp2.location = "Pune";
emp2.kinid = 30000;
emp2.managerKin = 007;
mng.kinid = 007;
mng.managername = "Gaurav";
mng2.kinid = 001;
mng2.managername = "Surya";
Dictionary<employee, manager> relations = new Dictionary<employee, manager>();
relations.Add(emp, mng);
relations.Add(emp2, mng2);
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine("The Manager details are :");
foreach (var element in relations)
Console.WriteLine(" \n KINID : {0} \n Manager'sName : {1}",element.Value.kinid, element.Value.managername);
Console.WriteLine("Enter the details of the manager..");
Console.ForegroundColor = ConsoleColor.Gray;
Console.Write("\nManager's Kin : ");
double mkin = Convert.ToDouble(Console.ReadLine());
Console.Write("Manager's Name : ");
string mname = Console.ReadLine();
manager mng1 = new manager();
mng1.kinid = mkin;
mng1.managername = mname;
int hashvalue = 17 * mname.GetHashCode() + mkin.GetHashCode();
#region BY USING FOREACH LOOP
int i = 0;
foreach (var element in relations)
{
if (element.Value.GetHashCode() == hashvalue)
{
i += 1;
if (i == 1)
{
Console.WriteLine("The Following employees report to the Manager : {0}", mname);
}
Console.WriteLine(element.Key.empname + " " + element.Key.kinid + " " + element.Key.location + " " + element.Key.managerKin);
}
}
if (i == 0)
{
Console.WriteLine("sorry the manager's details you entered \"{0}\" \"{1}\" does not exist in our database..", mng1.managername, mng1.kinid);
}
#endregion
Console.ReadLine();
}
}
For searching an object in a dictionary using the ContainsKey or ContainsValue keyword, compiler uses two implicit functions i.e. GetHashCode() and Equals(). So when we have an object for comparison, we need to Override both these methods !!
Here is the code
#region USING DICTIONARY TO STORE CLASS OBJECTS (check employee existence and print manager's name)
public class employee
{
public string empname { get; set; }
public string location { get; set; }
public double kinid { get; set; }
public double managerKin { get; set; }
//public override bool Equals(object obj) // ANY OF THE TWO EQUALS METHOD WORKS.
//{
// employee otheremployee;
// otheremployee = (employee)obj;
// return (otheremployee.kinid == this.kinid && otheremployee.location == this.location && otheremployee.empname == this.empname && otheremployee.managerKin == this.managerKin);
//}
public override bool Equals(object obj) //When Running this entire code, put a break-point on both the Equals() and GetHashCode() methods, and see the execution flow.
{
employee otheremployee;
otheremployee = (employee)obj;
return (obj.GetHashCode() == otheremployee.GetHashCode());
}
public override int GetHashCode() //When Running this entire code, put a break-point on both the Equals() and GetHashCode() methods, and see the execution flow.
{
//int temp = base.GetHashCode(); // DONT USE THIS
//return base.GetHashCode();
int temp = empname.GetHashCode() + location.GetHashCode() + kinid.GetHashCode() + managerKin.GetHashCode();
return temp;
}
}
public class manager
{
public string managername { get; set; }
public double kinid { get; set; }
public override int GetHashCode()
{
return base.GetHashCode();
}
public override bool Equals(object obj)
{
return base.Equals(obj);
}
}
public class program
{
public static void Main()
{
employee emp = new employee();
employee emp2 = new employee();
manager mng = new manager();
manager mng2 = new manager();
emp.empname = "Deepak";
emp.location = "Pune";
emp.kinid = 36885;
emp.managerKin = 007;
emp2.empname = "Astha";
emp2.location = "Pune";
emp2.kinid = 30000;
emp2.managerKin = 001;
mng.kinid = 007;
mng.managername = "Gaurav";
mng2.kinid = 001;
mng2.managername = "Surya";
Dictionary<employee, manager> relations = new Dictionary<employee, manager>();
relations.Add(emp, mng); // put a BreakPoint here and see the execution flow
relations.Add(emp2, mng2);// put a BreakPoint here and see the execution flow
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine("The Employee details are :");
foreach (var element in relations)
Console.WriteLine(" \n Employee Name : {0} \n Location : {1} \n Employee KinId : {2} \n Manager's KinId : {3} ",
element.Key.empname, element.Key.location, element.Key.kinid, element.Key.managerKin);
Console.WriteLine("Enter the details of the Employee..");
Console.ForegroundColor = ConsoleColor.Gray;
Console.Write("\nEmployee Name : "); string ename = Console.ReadLine();
Console.Write("Location : "); string elocn = Console.ReadLine();
Console.Write("Employee KinId : "); double ekinid = Convert.ToDouble(Console.ReadLine());
Console.Write("Manager's ID : "); double emngr = Convert.ToDouble(Console.ReadLine());
employee emp1 = new employee();
emp1.empname = ename;
emp1.location = elocn;
emp1.kinid = ekinid;
emp1.managerKin = emngr;
int i = 0; // This variable acts as a indicator to find whether the Employee Key exists or not.
if (relations.ContainsKey(emp1)) //Put a break point here and see the execution flow.
{
Console.WriteLine("the Employee : {0} exists..", emp1.empname);
Console.WriteLine("the Employee reports to the following manager : {0} \n and the Manager's KinId is {1}.", (relations[emp1]).managername, relations[emp1].kinid);
i = 1;
Console.ReadLine();
}
if (i == 0)
{
Console.WriteLine("the details of the employee named {0} does not exist !!", emp1.empname);
Console.ReadLine();
}
#endregion
To search element in dictionary you can use ContainsKey, ContainsValue methods or just write LINQ query
var dict = (from pair in relations
where pair.Value.Equals(mng1)
select pair).ToDictionary<employee,manager>();
In order to be able to compare 2 instances for equality, you should override the Equals method and, it is also good practice to implement IEquatable<T>.
When you override Equals, you should also override GetHashcode (this is used when you put your instances in a dictionary to calculate the bucket).
You should not use GetHashcode yourselves in order to compare 2 instances of your object for equality; instead you should use Equals (or an EqualityComparer, which will use the Equals method as well).
If you've implemented GetHashCode and Equals well, then you're able to determine whether a dictionary contains a specific instance by doing:
var myDictionary<int, Manager> = new Dictionary<int,Manager>();
myDictionary.ContainsKey (someKey)
or
var mySet = new HashSet<Manager>();
mySet.Contains(someManagerObject);
Dictionary.ContainsKey(employee) won't help here because the employee is the "unknown" value, and Contains won't help because it takes a KeyValuePair<employee,manager> and ... once again ... no employee is known. ContainsValue(manager) won't help because it doesn't return any key and because it's not a key, it is an O(n) operation, not an O(1) like ContainsKey!
With the current structure the only way is with some form of looping, although I would write it like this:
// Key is Employee, Value is Manager
// This is O(n)
var theEmployees = relations
.Where(rel => rel.Value.Equals(theManager))
.Select(rel => rel.Key);
This will only work after manager is given a valid Equals implementation. Note that the hash code is not used at all. (Because objects that are different may share the same hash-code, just comparing the hash-code is not a replacement for Equals, or ==, or CompareTo! -- depending on which one is appropriate.)
If there will be many such queries then the initial structure can be "inverted".
// Build a reverse lookup-up
var employeesForManager = relations
.GroupBy(rel => rel.Value) // group on Manager
.ToDictionary(g => g.Key, g => g); // Key is the group's Manager
// This is O(1), but only valid AFTER employeesForManager is [re-]generated
var theEmployees = employeesForManager[theManager]
This will only work if manager has a valid Equals and GetHashCode implementation. (GetHashCode is required because manager objects are used the key to the new Dictionary.)
As for which is "better" -- well, that depends. It is silly to create the reverse-lookup to only use it once, for instance. There is no performance problem until there is a performance problem: write clean code and profile.
Happy coding.
I believe you have a bug in your final response.
The line
return (obj.GetHashCode() == otheremployee.GetHashCode());
Probably should be
return (this.GetHashCode() == otheremployee.GetHashCode());
That way you are comparing the hash code for this object and the other object. As it is written in your response you appear to be comparing the other object to itself.
Related
I am having a bit of a frustrating time finding a simple method to compare and prove that the contents of two lists are equal. I have looked at a number of solutions on stackoverflow but I have not been successful. Some of the solutions look like they will require a large amount of work to implement and do something that on the face of it to my mind should be simpler, but perhaps I am too simple to realize that this cannot be done simply :)
I have created a fiddle with some detail that can be viewed here: https://dotnetfiddle.net/cvQr5d
Alternatively please find the full example below, I am having trouble with the object comparison method (variable finalResult) as it's returning false and if the content were being compared I would expect the value to be true:
using System;
using System.Collections.Generic;
using System.Linq;
public class ResponseExample
{
public Guid Id { get; set; } = Guid.Parse("00000000-0000-0000-0000-000000000000");
public int Value { get; set; } = 0;
public string Initials { get; set; } = "J";
public string FirstName { get; set; } = "Joe";
public string Surname { get; set; } = "Blogs";
public string CellPhone { get; set; } = "0923232199";
public bool EmailVerified { get; set; } = false;
public bool CellPhoneVerified { get; set; } = true;
}
public class Program
{
public static void Main()
{
var responseOne = new ResponseExample();
var responseTwo = new ResponseExample();
var responseThree = new ResponseExample();
var responseFour = new ResponseExample();
List<ResponseExample> objectListOne = new List<ResponseExample>();
objectListOne.Add(responseOne);
objectListOne.Add(responseTwo);
List<ResponseExample> objectListTwo = new List<ResponseExample>();
objectListTwo.Add(responseThree);
objectListTwo.Add(responseFour);
bool result = objectListOne.Count == objectListTwo.Count();
Console.WriteLine($"Count: {result}");
bool finalResult = ScrambledEquals<ResponseExample>(objectListOne, objectListTwo);
Console.WriteLine($"Object compare: {finalResult}");
}
//https://stackoverflow.com/a/3670089/3324415
public static bool ScrambledEquals<T>(IEnumerable<T> list1, IEnumerable<T> list2)
{
var cnt = new Dictionary<T,
int>();
foreach (T s in list1)
{
if (cnt.ContainsKey(s))
{
cnt[s]++;
}
else
{
cnt.Add(s, 1);
}
}
foreach (T s in list2)
{
if (cnt.ContainsKey(s))
{
cnt[s]--;
}
else
{
return false;
}
}
return cnt.Values.All(c => c == 0);
}
}
As people in comments have pointed out this will not work as comparing a complex type by default compares whether the reference is the same. Field by field comparison will not work without implementing equality methods (and then you would need to overload GetHashCode and so on). See https://learn.microsoft.com/en-us/dotnet/api/system.object.equals?view=net-5.0
However, if you can use c# 9, which is what you have in the fiddle you can define the type as a record instead of class. Records have built in field by field comparison. See https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/tutorials/records#characteristics-of-records
So public class ResponseExample would become public record ResponseExample and your code works as you expect.
Use Enumerable.All<TSource>(IEnumerable<TSource>, Func<TSource,Boolean>) Method which Determines whether all elements of a sequence satisfy a condition.
Once you have initilized your two List
list1.All(x=>list2.Contains(x))
This works by ensuring that all elements in list2 are containted in list1 otherwise returns false
Your method as is will compare if the 2 lists contain the same objects. So it is returning false as there are 4 different objects. If you create your list like this, using the same objects, it will return true:
List<ResponseExample> objectListOne = new List<ResponseExample>();
objectListOne.Add(responseOne);
objectListOne.Add(responseTwo);
List<ResponseExample> objectListTwo = new List<ResponseExample>();
objectListTwo.Add(responseTwo);
objectListTwo.Add(responseOne);
To get a true value when the contents of the objects are the same you could serialize the objects into a json string like this:
public static bool ScrambledEquals<T>(IEnumerable<T> list1, IEnumerable<T> list2)
{
JavaScriptSerializer json = new JavaScriptSerializer();
var cnt = new Dictionary<string,
int>();
foreach (T _s in list1)
{
string s = json.Serialize(_s);
if (cnt.ContainsKey(s))
{
cnt[s]++;
}
else
{
cnt.Add(s, 1);
}
}
foreach (T _s in list2)
{
string s = json.Serialize(_s);
if (cnt.ContainsKey(s))
{
cnt[s]--;
}
else
{
return false;
}
}
return cnt.Values.All(c => c == 0);
}
If the performance is not a big deal, you can use Newtonsoft.Json. We will be able to compare different types of objects as well as run a deep equals check.
First install the package:
Install-Package Newtonsoft.Json
Here is the code snip:
public static bool DeepEqualsUsingJson<T>(IList<T> l1, IList<T> l2)
{
if (ReferenceEquals(l1, l2))
return true;
if (ReferenceEquals(l2, null))
return false;
if (l1.Count != l2.Count)
return false;
var l1JObject = l1.Select(i => JObject.FromObject(i)).ToList();
var l2JObject = l2.Select(i => JObject.FromObject(i)).ToList();
foreach (var o1 in l1JObject)
{
var index = l2JObject.FindIndex(o2 => JToken.DeepEquals(o1, o2));
if (index == -1)
return false;
l2JObject.RemoveAt(index);
}
return l2JObject.Count == 0;
}
Is there a collection in C# that guarantees me that I will have only unique elements? I've read about HashSet, but this collection can contain duplicates. Here is my code:
public class Bean
{
public string Name { get; set; }
public int Id { get; set; }
public override bool Equals(object obj)
{
var bean = obj as Bean;
if (bean == null)
{
return false;
}
return this.Name.Equals(bean.Name) && this.Id == bean.Id;
}
public override int GetHashCode()
{
return Name.GetHashCode() * this.Id.GetHashCode();
}
}
You may complain about using non-readonly properties in my GetHashCode method, but this is a way of doing (not the right one).
HashSet<Bean> set = new HashSet<Bean>();
Bean b1 = new Bean {Name = "n", Id = 1};
Bean b2 = new Bean {Name = "n", Id = 2};
set.Add(b1);
set.Add(b2);
b2.Id = 1;
var elements = set.ToList();
var elem1 = elements[0];
var elem2 = elements[1];
if (elem1.Equals(elem2))
{
Console.WriteLine("elements are equal");
}
And in this case, my set contains duplicates.
So is there a collection in C# that guarantees me that it does not contains duplicates?
So is there a collection in C# that guarantees me that it does not
contains duplicates?
There is no existing collection class in C# that does this. You could write your own, but there is no existing one.
Some extra information regarding the issue you are experiencing
If you change a HashSet entry after adding it to the HashSet, then you need to regenerate the HashSet. My below RegenerateHashSet can be used to do that.
The reason you need to regenerate is that duplicate detection only occurs at insertion time (or, in other words, it relies on you not changing an object after you insert it). Which makes sense, if you think about it. The HashSet has no way to detect that an object it contains has changed.
using System;
using System.Collections.Generic;
using System.Linq;
namespace Test
{
public static class HashSetExtensions
{
public static HashSet<T> RegenerateHashSet<T>(this HashSet<T> original)
{
return new HashSet<T>(original, original.Comparer);
}
}
public class Bean
{
public string Name { get; set; }
public int Id { get; set; }
public override bool Equals(object obj)
{
var bean = obj as Bean;
if (bean == null)
{
return false;
}
return Name.Equals(bean.Name) && Id == bean.Id;
}
public override int GetHashCode()
{
return Name.GetHashCode() * Id.GetHashCode();
}
}
public class Program
{
static void Main(string[] args)
{
HashSet<Bean> set = new HashSet<Bean>();
Bean b1 = new Bean { Name = "n", Id = 1 };
Bean b2 = new Bean { Name = "n", Id = 2 };
set.Add(b1);
set.Add(b2);
b2.Id = 1;
var elements = set.ToList();
var elem1 = elements[0];
var elem2 = elements[1];
if (elem1.Equals(elem2))
{
Console.WriteLine("elements are equal");
}
Console.WriteLine(set.Count);
set = set.RegenerateHashSet();
Console.WriteLine(set.Count);
Console.ReadLine();
}
}
}
Note that the above technique is not bullet-proof - if you add two objects (Object A and Object B) which are duplicates and then change Object B to be different to Object A then the HashSet will still only have one entry in it (since Object B was never added). As such, what you probably want to do is actually store your complete list in a List instead, and then use new HashSet<T>(yourList) whenever you want unique entries. The below class may assist you if you decide to go down that route.
public class RecalculatingHashSet<T>
{
private List<T> originalValues = new List<T>();
public HashSet<T> GetUnique()
{
return new HashSet<T>(originalValues);
}
public void Add(T item)
{
originalValues.Add(item);
}
}
If you don't write your own collection type and handle property changed events to re-evaluate the items, you need to re-evaluate the items at each access. This can be accomplished with LINQ deferred execution:
ICollection<Bean> items= new List<Bean>();
IEnumerable<Bean> reader = items.Distinct();
Rule: only use items to insert or remove elements, use reader for any read access.
Bean b1 = new Bean { Name = "n", Id = 1 };
Bean b2 = new Bean { Name = "n", Id = 2 };
items.Add(b1);
items.Add(b2);
b2.Id = 1;
var elements = reader.ToList();
var elem1 = elements[0];
var elem2 = elements[1]; // throws exception because there is only one element in the result list.
I have a class named Person.
Which has data members as follows:
Boolean Old
Boolean Educated
Boolean Employed
Boolean Married
Now I have a string array 'Values' which contains some of these data members.
My task is to set data members present in an array 'Values' to true for a particular object of class Person.
As in I would be having one array for each of the objects p1,p2,p3... of class Person.
For now I am using 'if' condition for each of the data members to check whether they are present in an array. And setting them to true if they do.
How can I use these string values to set data members to true in more efficient way?
Eg. p1 and p2 are objects of class Person
For p1
Values = {Old, Employed}
In this case I will set p1.Old and p1.Employed to true.
For p2
Values= {Employed,Married}
In this case I will set p2.Employed and p2.Married to true.
Person setMembers(string[] Values)
{
var p1 = new Person();
string s1 = "Old";
int i1 = Array.IndexOf(Values, s1);
string s2 = "Educated";
int i2 = Array.IndexOf(Values, s2);
string s3 = "Employed";
int i3 = Array.IndexOf(Values, s3);
string s4 = "Married";
int i4 = Array.IndexOf(Values, s4);
if (i1 > -1)
{p1.Old = true;};
if (i2 > -1)
{p1.Educated = true;};
if (i3 > -1)
{p1.Employed = true;};
if (i4 > -1)
{p1.Married = true;};
return p1;
}
One way to optimize conversion of string values to setting properties would be a hash dictionary that maps strings to actions, like this:
private static readonly IDictionary<string,Action<Person>> ActionByName =
new Dictionary<string,Action<Person>>() {
{"Educated", p => { p.Educated = true; } }
, {"Employed", p => { p.Employed = true; } }
, {"Married", p => { p.Married = true; } }
, {"Old", p => { p.Old = true; } }
};
With this dictionary in hand, you could process the values like this:
Person p1 = ...
foreach (var val in valuesForP1) {
Action<Person> a;
if (ActionByName.TryGetValue(val, out a)) {
a(p1);
}
}
This way lookup of the thing to do is performed using hash table. Note that you can skip the dictionary and use switch instead, like this:
foreach (var val in valuesForP1) {
switch(val) {
case "Educated": p1.Educated = true; break;
case "Employed": p1.Employed = true; break;
case "Married": p1.Married = true; break;
case "Old": p1.Old = true; break;
}
}
If you can use System.Linq, then you would have Contains function and then you can do something like this
objPerson.Old = Values.Contains("Old");
objPerson.Educated = Values.Contains("Educated");
With reflection... but the string values have to match exactly the property names
class Program
{
static void Main(string[] args)
{
var testPerson = CreatePerson(new[] { "Old", "Employed" });
Console.WriteLine(testPerson.Old);
Console.WriteLine(testPerson.Educated);
Console.WriteLine(testPerson.Employed);
Console.WriteLine(testPerson.Married);
}
public static Person CreatePerson(string[] args)
{
Person result = new Person();
Type personType = typeof(Person);
foreach (string item in args)
{
personType.GetProperty(item).SetMethod.Invoke(result, new object[] { true });
}
return result;
}
}
public class Person
{
public bool Old { get; set; }
public bool Educated { get; set; }
public bool Employed { get; set; }
public bool Married { get; set; }
}
I think you may write a method that sets your properties accordingly:
class Person {
bool old, educated, employed, married;
void SetValues(string[] values) {
this.old = values.Contains("Old");
this.educated = values.Contains("Educated");
this.employed = values.Contains("Employed");
this.married = values.Contains("Married");
}
Now you may call this as follows:
Person p1 = new Person();
p.SetValues(new[] { "Old", "Married" };
I have a question about the following code:
private void Filter (object sender, Android.Text.TextChangedEventArgs e)
{
List<Animal> animalList = new List<Animal>();
if(!string.IsNullOrEmpty(_editText.Text))
{
foreach (string str in _animalList)
{
if (str.Contains(_editText.Text))
{
animalList.Add (str);
}
}
}
_listView.Adapter = new AnimalAdapter(this, _animalList = animalList);
}
The Animal class:
public class Animal
{
private readonly int _intKey;
public int AnimalNumber { get; private set; }
public int StableNumber { get; private set; }
public int LactoseNumber { get; private set; }
public Animal ( int intKey, int animalNumber, int stableNumber, int lactoseNumber )
{
_intKey = intKey;
AnimalNumber = animalNumber;
StableNumber = stableNumber;
LactoseNumber = lactoseNumber;
}
public override string ToString ()
{
return "Number: " + AnimalNumber + "\nGroup: " + StableNumber + "\nLactation: " + LactoseNumber;
}
}
Declaration of _animalList:
private List<Animal> _animalList;
i need to check if the _animalList Contains the input of the _editText.Text.
But _animalList isn't a string so i need to use a var.
Is there something like a var.Contains or do i have to use something else?
Contains method is available for string type. You will need to cast your object to string.
A/c to your class definition you should do like:
foreach (Animal str in _animalList)
{
if (str.ToString().Contains(_editText.Text)) //using user defined "ToString()"
{
animalList.Add (str);
}
}
You can also check individual properties:
foreach (Animal str in _animalList)
{
if (str.AnimalNumber.ToString().Contains(_editText.Text)) //if "AnimalNumber" is like "_editText.Text"
{
animalList.Add (str);
}
}
Instead of trying to filter using ToString, it would be better to use the real property values. For example:
var number = Convert.ToInt32(_editText.Text);
var filteredList = _animalList
.Where(x => x.AnimalNumber == number ||
x.StableNumber == number ||
x.LactoseNumber == number)
.ToList();
Otherwise, user could type "Number" and since your ToString override contains that string, all of the items in the list would match positively.
(I didn't include any validation or error checking in the code above, so you should consider those as well).
var inputText = _editText.Text;
int enteredNumber;
// you should make sure that the inputText is always an int
var isInt = int.TryParse(inputText, out enteredNumber);
//for example, if you are going to find by AnimalNumber, which is an int, you can use this. .
if (isInt){
foreach (var animal in _animalList){
var animalNumber = animal.AnimalNumber;
if (animalNumber == enteredNumber)
{
animalList.Add(animal);
}
}
}
Edit (LINQ alternative):
if (isInt){
animalList.AddRange(from animal in _animalList
let animalNumber = animal.AnimalNumber
where animalNumber == enteredNumber
select animal);
}
_animalList.Select(a => a.ToString()).Contains(_editText.Text)
This expression returns true if the output of the ToString method of any animal object equals _editText.Text.
_animalList.Select(a => a.ToString()).Any(str => str.Contains(_editText.Text))
This expression returns true if the output of the ToString method of any animal object contains _editText.Text (as a substring). This is equivalent to Shaharyar's answer.
var animalList = _animalList.Where(a => a.ToString().Equals(_editText.Text)).ToList();
var animalList = _animalList.Where(a => a.ToString().Contains(_editText.Text)).ToList();
These statements filter the input list directly.
I'm new to C# & having trouble wi/ this current assignment. I need to use a Delegate to sort Sort the employees by social security number in descending order and by last name in ascending order. If anyone can just point me in how to get started it will help greatly. This is just the employee class, but if needed, I can post all the classes.
using System;
// Fig. 12.4: Employee.cs
// Employee abstract base class.
using System.Text;
public abstract class Employee : IPayable
{
// read-only property that gets employee's first name
public string FirstName { get; private set; }
// read-only property that gets employee's last name
public string LastName { get; private set; }
// read-only property that gets employee's social security number
public string SocialSecurityNumber { get; private set; }
// three-parameter constructor
public Employee( string first, string last, string ssn )
{
FirstName = first;
LastName = last;
SocialSecurityNumber = ssn;
} // end three-parameter Employee constructor
// return string representation of Employee object, using properties
public override string ToString()
{
return string.Format( "{0} {1}\nsocial security number: {2}",
FirstName, LastName, SocialSecurityNumber );
} // end method ToString
// abstract method overridden by derived classes
public abstract decimal GetPaymentAmount(); // no implementation here
} // end abstract class Employee
// Fig. 12.15: PayableInterfaceTest.cs
// Tests interface IPayable with disparate classes.
using System;
public class PayableInterfaceTest
{
public static void Main( string[] args )
{
// create four-element IPayable array
IPayable[] payableObjects = new IPayable[8];
// populate array with objects that implement IPayable
payableObjects[0] = new SalariedEmployee("John", "Smith", "111-11-1111", 700M);
payableObjects[1] = new SalariedEmployee("Antonio", "Smith", "555-55-5555", 800M);
payableObjects[2] = new SalariedEmployee("Victor", "Smith", "444-44-4444", 600M);
payableObjects[3] = new HourlyEmployee("Karen", "Price", "222-22-2222", 16.75M, 40M);
payableObjects[4] = new HourlyEmployee("Ruben", "Zamora", "666-66-6666", 20.00M, 40M);
payableObjects[5] = new CommissionEmployee("Sue", "Jones", "333-33-3333", 10000M, .06M);
payableObjects[6] = new BasePlusCommissionEmployee("Bob", "Lewis", "777-77-7777", 5000M, .04M, 300M);
payableObjects[7] = new BasePlusCommissionEmployee("Lee", "Duarte", "888-88-888", 5000M, .04M, 300M);
Console.WriteLine(
"Lab 2 output:\n" );
// generically process each element in array payableObjectsWW
foreach ( var currentPayable in payableObjects )
{
// output currentPayable and its appropriate payment amount
Console.WriteLine( "payment due {0}: {1:C}\n",
currentPayable, currentPayable.GetPaymentAmount() );
} // end foreach
} // end Main
} // end class PayableInterfaceTest
Why don't you use LINQ ?
var sortedArray = payableObjects
.OrderByDescending(e => e.SocialSecurityNumber)
.ThenBy(e => e.LastName)
.ToArray();
Array.Sort(T[], delegate int(T, T)) is probably what you're looking for.
Call Array.Sort with your array as the first parameter, and create a function in your Employee that fills delegate int(Employee, Employee). If I recall correctly, that function needs to return 0 if they are equal, a negative number if the first comes first ascending, and a positive number if the first comes second ascending.
//to order by SSN desc
foreach (var e in payableObjects.OrderByDescending(e => e.SocialSecurityNumber))
{
Console.WriteLine(e.FirstName + " " + e.LastName + " " + e.SocialSecurityNumber);
}
Console.WriteLine("");
//to order by Lastname Asc
foreach (var e in payableObjects.OrderBy(e => e.LastName))
{
Console.WriteLine(e.FirstName + " " + e.LastName + " " + e.SocialSecurityNumber);
}