Good day!
I have a List of ValueObj:
class ValueObj
{
int ID;
float value;
}
How to get binary search objects by id?
(List tempValues)
I make ValueComparer class,but dont know am i right?
class ValueComparer<ValueObj>
{
public int Compare(ValueObjx, ValueObjy)
{
if (x == y) return 0;
if (x == null) return -1;
if (y == null) return 1;
return -1; ///???
}
}
I need to sort List by ID. Like that?:
tempValues.Sort(new ValueComparer());
And how to use BinarySearch?
first of all you should make your class like this.
your fields were not public and you can not access them,
also public fields are not good so you should change them to property
class ValueObj
{
public int ID { get; set; }
public float value { get; set; };
}
and your comparer like this
class ValueComparer : IComparable<ValueObj>
{
public int Compare(ValueObj x, ValueObj y)
{
if (ReferenceEquals(x, y)) return 0;
if (x == null) return -1;
if (y == null) return 1;
return x.ID == y.ID ? 0 :
x.ID > y.ID ? 1 : -1;
}
}
then you have a list like
var tempValues = new List<ValueObj>();
//many items are added here
you should always sort your list before performing a binary serach
//this does not modify the tempValues and generate a new sorted list
var sortedList = tempValues.OrderBy(x => x.ID).ToList();
or you can sort tempValues directly
//tempValues is modified in this method and order of items get changed
tempValues.Sort(new ValueComparer<ValueObj>());
now you want to find index of specific ValueObj
var index = sortedList.BinarySearch(specificValueObj, new ValueComparer<ValueObj>());
or if you have used second method of sorting
var index = tempValues.BinarySearch(specificValueObj, new ValueComparer<ValueObj>());
The List class in C# has a BinarySearch method you can use with your Comparable.
Your type:
class ValueObj
{
public int ID{ get; set;}
public float value { get; set;}
}
Your comparison class (do not forget to implement the correct interface!):
class ValueObjIDComparer : IComparable<ValueObj>
{
public int Compare(ValueObj x, ValueObj y)
{
if (x == null) return -1;
if (y == null) return 1;
if (x.ID == y.ID) return 0;
return x.ID > y.ID ? 1 : -1;
}
}
Executing a binary search:
List<ValueObj> myList = new List<ValueObj>();
myList.Add(new ValueObj(){ID=1});
myList.Add(new ValueObj(){ID=2});
// ...
int idToFind = 2;
myList.Sort(new ValueObjIDComparer());
int indexOfItem = myList.BinarySearch(idToFind, new ValueObjIDComparer());
There are many more operations you can perform on lists. See the documentation here.
If you want to sort by ID, you could simply compare the IDs in Compare method:
return x.ID > y.ID;
Related
public static int IndexOf(Product[] products, Predicate<Product> predicate)
{
if (products == null)
{
throw new ArgumentNullException();
}
for (int i = 0; i <= products.Length - 1; i++)
{
if (predicate == null)
{
throw new ArgumentNullException();
}
Product product = products[i];
if (predicate(product))
{
return i;
}
}
return -1;
}
Searches for the index of a product in an products based on a predicate
products Products used for searching
predicate Product predicate
If match found then returns index of product in products
otherwise -1
I am asked to make changes only in the IndexOf(Product[] products, Predict predict) method without touching the Product model.
[Test]
public void IndexOf_Products_ReturnsTwo()
{
var products = new Product[]
{
new Product("Product 1", 10.0d),
new Product("Product 2", 20.0d),
new Product("Product 3", 30.0d),
};
var productToFind = new Product("Product 3", 30.0d);
int index = Utilities.IndexOf(products, product => product.Equals(productToFind));
Assert.That(index, Is.EqualTo(2));
}
Expected: 2 But was:-1
public class Product
{
public Product(string name, double price)
{
Name = name;
Price = price;
}
public string Name { get; set; }
public double Price { get; set; }
}
Well IndexOf is a correct implementation, which can be make to more general:
public static int IndexOf<T>(IEnumerable<T> source, Predicate<T> predicate)
{
if (source is null)
{
throw new ArgumentNullException(nameof(source));
}
if (predicate == null)
{
throw new ArgumentNullException(nameof(predicate));
}
int index = 0;
foreach (T item in source) {
if (predicate(item))
return index;
index += 1;
}
return -1;
}
The actual problem seems to be with Product class which doesn't override Equals and GetHashCode methods.
Without Equals and GetHashCode .net compare references (which are different), not values.
To compare by values you should explain .net how to do it, something like this:
// Let Product be equatable with Product - IEquatable<Product>
public class Product : IEquatable<Product> {
...
// Let .net know how to compare for equality:
//TODO: put the right names for Name and Price
public bool Equals(Product other) => other != null &&
other.Name == Name &&
other.Price == Price;
public override bool Equals(object o) => o is Product other && Equals(other);
public override int GetHashCode() => HashCode.Combine(Name, Price);
}
Edit: if you can't change Product class, you have to change predicate and explain there how to compare for equality:
int index = Utilities
.IndexOf(products, product => productToFind.Name == product.Name &&
productToFind.Price == product.Price);
Please, fiddle youself.
I want to sort my List, where T is Products.
The List may contains elememts with duplicate ReportSeqId. I want to sort it according to ReportSeqId.
But the criteria is that if the ReportSeqId = 0 then it should come last.
INPUT :
new ilistProd<Products>()
{
new Products(0, Report1, SSR),
new Products(2, Report2, SBO),
new Products(0, Report3, PST),
new Products(3, Report4, ABR),
new Products(1, Report5, OSS),
new Products(0, Report6, TCP),
}
OUTPUT:
new ilistProd<Products>()
{
new Products(1, Report5, OSS),
new Products(2, Report2, SBO),
new Products(3, Report4, ABR),
new Products(0, Report3, PST),
new Products(0, Report6, TCP),
new Products(0, Report1, SSR)
}
Below is my code :
public class Products
{
//ctor
public SDVar(int xiReportSeqId, string xiReportName, string xiProduct)
{
this.ReportSeqId = xiReportSeqId;
this.ReportName = xiReportName;
this.Product = xiProduct;
}
public int ReportSeqId {get; set;}
public string ReportName {get; set;}
public string Product {get; set;}
}
public class SDVar
{
//ctor
public SDVar()
{
}
public void DoSort(ref List<Products> ilistProd)
{
ilistProd.Sort(delegate(Products x, Products y)
{
if (x.ReportSeqId == 0)
{
if (y.ReportSeqId == 0)
{
return 0;
}
return -1;
}
return x.ReportSeqId.CompareTo(y.ReportSeqId);
}
}
}
Try this
list.Sort(delegate(Products x, Products y)
{
if(x.ReportSeqId == 0)
return 1;
if(y.ReportSeqId == 0)
return -1;
return x.ReportSeqId.CompareTo(y.ReportSeqId);
});
Normally my preferred solution would be to add an extra property (e.g. SortIndex) which can be used in either Linq, or in a sort delegate (where id 0 would return an int.maxvalue), but to get the existing code to work, you should do an extra check to see of the second id is 0, if the first id is not:
if (x.ReportSeqId == 0)
{
if (y.ReportSeqId == 0)
{
return 0;
}
return 1;
}
else if (y.ReportSeqId == 0)
return -1;
return x.ReportSeqId.CompareTo(y.ReportSeqId);
Another way is to implement IComparable
public class Product : IComparable<Product>
{
private int ReportSeqId = 0;
public int CompareTo(Product other)
{
if (ReportSeqId == 0 || other == null) return 1;
if (other.ReportSeqId == 0) return - 1;
return ReportSeqId - other.ReportSeqId;
}
}
Using LINQ:
products = products.OrderBy(p => p.ReportSeqId == 0 ? Int32.MaxValue : p.ReportSeqId).ToList();
I have two Lists
ListA<Emp> and ListB<Emp>
both are having 1000 records.
Emp is an object of Employee Class. Below is my Employee class
public class Employee
{
int ID = 0;
string Name = String.Empty;
string Dept = String.Empty;
string Address = String.Empty;
int Age = 0;
string Email = String.Empty;
}
I want to verify if both the Lists are equal. The Emp objects may be placed in different order. Also, there might be several Emp objects which are having exactly same info in both the list. I have to verify those also.
I tried to sort the lists and compared using SequenceEqual
Enumerable.SequenceEqual(ListA.OrderBy(s => s), ListB.OrderBy(s => s)
I am getting below error
At least one object must implement IComparable.
Exception Stack trace is as below
at System.Collections.Comparer.Compare(Object a, Object b)
at System.Collections.Generic.ObjectComparer`1.Compare(T x, T y)
at System.Linq.EnumerableSorter`2.CompareKeys(Int32 index1, Int32 index2)
at System.Linq.EnumerableSorter`1.QuickSort(Int32[] map, Int32 left, Int32 right)
at System.Linq.EnumerableSorter`1.Sort(TElement[] elements, Int32 count)
at System.Linq.OrderedEnumerable`1.<GetEnumerator>d__0.MoveNext()
at System.Linq.Enumerable.SequenceEqual[TSource](IEnumerable`1 first, IEnumerable`1 second, IEqualityComparer`1 comparer)
at System.Linq.Enumerable.SequenceEqual[TSource](IEnumerable`1 first, IEnumerable`1 second)
How can I implement this ? Also it will be better if you guys can provide me the fastest way of doing this because the number of objects in List may grow to 10 million.
Thanks for your help !
EDIT: Every employee must be in both list, order does not matter. But, if ListA contains same employee object 5 times (that means some duplicate entries), and ListB contains the employee object 4 times, then ListA and ListB are not equal.
You can use SequenceEqual with a custom IEqualityComparer<Employee>:
class EmployeeComparer : IEqualityComparer<Employee>
{
public bool Equals(Employee x, Employee y)
{
if (x == null || y == null) return false;
bool equals = x.ID==y.ID && x.Name == y.Name && x.Dept == y.Dept
&& x.Address == y.Address && x.Age == y.Age && x.Email == y.Email;
return equals;
}
public int GetHashCode(Employee obj)
{
if (obj == null) return int.MinValue;
int hash = 19;
hash = hash + obj.ID.GetHashCode();
hash = hash + obj.Name.GetHashCode();
hash = hash + obj.Dept.GetHashCode();
hash = hash + obj.Address.GetHashCode();
hash = hash + obj.Age.GetHashCode();
hash = hash + obj.Email.GetHashCode();
return hash;
}
}
Now it's so simple:
listA.SequenceEqual(ListB, new EmployeeComparer());
If the order is not important and you only want to know if all employees are in both lists you can use HashSet<Employee>.SetEquals to determine if both lists contain the same people:
var empComparer = new EmployeeComparer();
bool bothEqual = new HashSet<Employee>(ListA, empComparer)
.SetEquals(new HashSet<Employee>(ListB, empComparer));
Best complexity is O(N)
Following realization with using HashSet:
Class with implementation of GetHashCode and Equals:
public class Employee
{
public int ID = 0;
public string Name = String.Empty;
public string Dept = String.Empty;
public string Address = String.Empty;
public int Age = 0;
public string Email = String.Empty;
public override int GetHashCode()
{
return
ID.GetHashCode() ^
(Name ?? String.Empty).GetHashCode() ^
(Dept ?? String.Empty).GetHashCode() ^
(Address ?? String.Empty).GetHashCode() ^
Age.GetHashCode() ^
(Email ?? String.Empty).GetHashCode()
;
}
public override bool Equals(object obj)
{
Employee other = obj as Employee;
if (obj == null)
return false;
return ID == other.ID &&
Name == other.Name &&
Dept == other.Dept &&
Address == other.Address &&
Age == other.Age &&
Email == other.Email;
}
}
Function to compare lists:
public static bool CompareLists(List<Employee> list1, List<Employee> list2)
{
if (list1 == null || list2 == null)
return list1 == list2;
if (list1.Count != list2.Count)
return false;
Dictionary<Employee, int> hash = new Dictionary<Employee, int>();
foreach (Employee employee in list1)
{
if (hash.ContainsKey(employee))
{
hash[employee]++;
}
else
{
hash.Add(employee, 1);
}
}
foreach (Employee employee in list2)
{
if (!hash.ContainsKey(employee) || hash[employee] == 0)
{
return false;
}
hash[employee]--;
}
return true;
}
If the numbers in the list are going to grow enormous (10M), you are probably going to have to consider parallelization of the look-up to get an acceptable query time.
Consider using PLINQ.
Some more clarity on what you mean by 'equal' would be good. How complex is the equivalence check? Are you checking that the objects are the same or that the objects values are the same?
Another consideration would be this; if the number of elements are going to become large, could you consider moving this check down from .NET into your database - perhaps as a stored procedure? You may find it executes more efficiently there.
reduce the list to a scalar type: int, string, ....
L1.Select(x => x.K).ToArray()
use the except method
L1.Select(x => x.K).ToArray().Except(L1.Select(x => x.K).ToArray())
If the count of the resulting set is 0 then the List are equals
L1.Select(x => x.K).ToArray().Except(L1.Select(x => x.K).ToArray()).Count()
All together
public class Program {
public static void Main(String[] args) {
List<O> L1 = new List<O>{
new O {K = 1, V = "abcd"},
new O {K = 2, V = "efgh"}
};
List<O> L2 = new List<O>{
new O {K = 1, V = "abcd"}
};
List<O> L3 = new List<O>{
new O {K = 1, V = "abcd"},
new O {K = 3, V = "ijkl"}
};
List<O> L4 = new List<O>{
new O {K = 2, V = "efgh"},
new O {K = 1, V = "abcd"}
};
Console.WriteLine(L1.Select(x => x.K).ToArray().Except(L1.Select(x => x.K).ToArray()).Count());
Console.WriteLine(L1.Select(x => x.K).ToArray().Except(L2.Select(x => x.K).ToArray()).Count());
Console.WriteLine(L1.Select(x => x.K).ToArray().Except(L3.Select(x => x.K).ToArray()).Count());
Console.WriteLine(L1.Select(x => x.K).ToArray().Except(L4.Select(x => x.K).ToArray()).Count());
}
}
public class O {
public int K { get; set; }
public String V { get; set; }
}
Exactly what it says.
Implement IComparable on the class Employee
Also need to override Equals
Due to potentially a large number of calls to GetHashCode save it and only calculate on changes.
Tested
IComparable Interface
public MainWindow()
{
InitializeComponent();
List<Person> PLa = new List<Person>();
List<Person> PLb = new List<Person>();
PLa.Add(new Person { Age = 3, Name = "Jim"});
PLa.Add(new Person { Age = 2, Name = "Jimmmy" });
PLa.Add(new Person { Age = 1, Name = "Jim" });
PLb.Add(new Person { Age = 1, Name = "Jim" });
PLb.Add(new Person { Age = 3, Name = "Jim" });
PLb.Add(new Person { Age = 2, Name = "Jimmmy" });
System.Diagnostics.Debug.WriteLine(ListSameIgnoreOrder(PLa, PLb));
}
public bool ListSameIgnoreOrder(List<Person> PLa, List<Person> PLb)
{
if (PLa.Count != PLb.Count) return false;
//PLa.Sort();
//PLb.Sort();
return Enumerable.SequenceEqual(PLa.OrderBy(s => s), PLb.OrderBy(s => s));
//for (int i = 0; i < PLa.Count; i++)
//{
// System.Diagnostics.Debug.WriteLine(
// PLa[i].Age.ToString() + " " + PLb[i].Age.ToString() + " " +
// PLa[i].Name + " " + PLb[i].Name);
// if (!PLa[i].Equals(PLb[i])) return false;
//}
//return true;
}
public class Person : object, IComparable
{
private int age = 0;
private string name = string.Empty;
private int hash;
public int Age
{
get { return age; }
set
{
if (age == value) return;
age = value;
CalcHash();
}
}
public string Name
{
get { return name; }
set
{
if (name == value) return;
name = value;
CalcHash();
}
}
public override bool Equals(Object obj)
{
//Check for null and compare run-time types.
if (obj == null || !(obj is Person)) return false;
Person f = (Person)obj;
if (f.Age != this.Age) return false;
return (string.Compare(f.name, this.name) == 0);
}
private void CalcHash()
{
hash = Age.GetHashCode() ^
(Name ?? String.Empty).GetHashCode();
}
public override int GetHashCode()
{
return hash;
//return age ^ name.GetHashCode();
}
public int CompareTo(object obj)
{
if (obj == null) return 1;
Person otherPerson = obj as Person;
if (otherPerson != null)
{
if (otherPerson.Age > this.Age) return -1;
if (otherPerson.Age < this.Age) return 1;
// compare all properties like above
return string.Compare(otherPerson.name, this.name);
}
else
throw new ArgumentException("Object is not a Person");
}
public Person() { CalcHash(); }
}
This works.
public bool EqualList(Dictionary<int, string> a, Dictionary<int, string> b)
{
if (a.Count == b.Count)
{
bool rs = false;
foreach (var i in a)
{
if (b.ContainsKey(i.Key))
{
rs = true;
}
else
{
rs = false;
break;
}
}
return rs;
}
else
{
return false;
}
Usage:
if(EqualList(List<A>.ToDictionary(k => k.Key, k => k.Value), List<B>.ToDictionary(k => k.Key, k => k.Value)){
}else{
}
I have two custom classes.
The first class contains basic data:
public class Request
{
public Request(int lineID, string partNo, int qty, int reasonID, int typeID)
{
LineID = lineID;
PartNo = partNo;
Qty = qty;
ReasonID = reasonID;
TypeID = typeID;
}
public int LineID { get; private set; }
public string PartNo { get; private set; }
public int Qty { get; internal set; }
public int ReasonID { get; private set; }
public int TypeID { get; private set; }
}
The second class contains a List of these Request objects, with a signature as follows:
public class Requests : IEnumerable<Request>
{
private List<Request> list;
public Requests()
{
list = new List<Request>();
}
public int Add(Request item)
{
if (item != null)
{
foreach (var x in list.Where(r =>
(r.LineID == item.LineID) &&
(r.PartNo == item.PartNo) &&
(r.ReasonID == item.ReasonID) &&
(r.TypeID == item.TypeID)))
{
x.Qty += item.Qty;
return list.IndexOf(x);
}
list.Add(item);
return list.Count - 1;
}
return -1;
}
// other code
}
I am testing my code and adding items is putting new items into the list, but the LINQ query to find duplicates is not working.
If 2 identical items are added to the list, I want my code to be smart enough to simply update the quantity, but it does not seem to be working.
Could someone tell me what is wrong with the LINQ query?
Could someone tell me what is wrong with the LINQ query?
Theoretically it looks OK. I think we need to know more information about your data to be able to find out why it isn't working as you expect. Does the combination of LineID, PartNo, ReasonID, and TypeID uniquely distinguish an item? Since PartNo is a string, are the values case-insensitive (your comparison is case-sensitive)?
If 2 identical items are added to the list, I want my code to be smart enough to simply update the quantity, but it does not seem to be working.
For this I would suggest a different approach. Consider overriding Equals() on your Request type. Then your Add method can just check if the list already contains the item, incrementing the quantity if so and adding it if not:
var idx = list.IndexOf(item);
if(idx != -1)
{
list[idx].Qty += item.Qty;
}
else
{
list.Add(item);
}
Make Request implement IEquatable<Request>, because this is what IndexOf uses:
public bool Equals(Request other) {
return other != null && (this.LineID == other.LineID) && (this.PartNo == other.PartNo) && (this.ReasonID == other.ReasonID) && (this.TypeID == other.TypeID);
}
Then:
public int Add(Request item) {
if (item != null)
{
int ind = list.IndexOf(item);
if (ind == -1)
{
list.Add(item);
return list.Count - 1;
}
else
{
list[ind].Qty += item.Qty;
return ind;
}
}
return -1;
}
You can modify the method by materializing LINQ query. For example:
public int Add(Request item) {
if (item != null) {
foreach (var x in list.Where(r =>
(r.LineID == item.LineID) &&
(r.PartNo == item.PartNo) &&
(r.ReasonID == item.ReasonID) &&
(r.TypeID == item.TypeID)
).ToList()) {
x.Qty += item.Qty;
return list.IndexOf(x);
}
list.Add(item);
return list.Count - 1;
}
return -1;
}
But, because your Requests must be unique, you can use this
public int Add(Request item)
{
if (item != null)
{
var req = list.SingleOrDefault(r =>
(r.LineID == item.LineID) &&
(r.PartNo == item.PartNo) &&
(r.ReasonID == item.ReasonID) &&
(r.TypeID == item.TypeID)
);
if(req!=null)
{
req.Qty += item.Qty;
return list.IndexOf(req);
}
list.Add(item);
return list.Count - 1;
}
return -1;
}
I want to remove duplicates in a list using following code, but it does not work. Anyone could enlighten me? Thanks.
public sealed class Pairing
{
public int Index { get; private set; }
public int Length { get; private set; }
public int Offset { get; private set; }
public Pairing(int index, int length, int offset)
{
Index = index;
Length = length;
Offset = offset;
}
}
class MyComparer : IEqualityComparer<Pairing>
{
public bool Equals(Pairing x, Pairing y)
{
return ((x.Index == y.Index) && (x.Length == y.Length) && (x.Offset == y.Offset));
}
public int GetHashCode(Pairing obj)
{
return obj.GetHashCode();
}
}
class Program
{
static void Main(string[] args)
{
List<Pairing> ps = new List<Pairing>();
ps.Add(new Pairing(2, 4, 14));
ps.Add(new Pairing(1, 2, 4));
ps.Add(new Pairing(2, 4, 14));
var unique = ps.Distinct(new MyComparer());
foreach (Pairing p in unique)
{
Console.WriteLine("{0}\t{1}\t{2}", p.Index, p.Length, p.Offset);
}
Console.ReadLine();
}
}
According to the example on the IEnumerable.Distinct page you will need to implement GetHashCode() so that the equal objects return the same hashcode. If you do not override GetHashCode() in your object it is not guaranteed to return the same hashcode.
// If Equals() returns true for a pair of objects
// then GetHashCode() must return the same value for these objects.
public int GetHashCode(Product product)
{
//Check whether the object is null
if (Object.ReferenceEquals(product, null)) return 0;
//Get hash code for the Name field if it is not null.
int hashProductName = product.Name == null ? 0 : product.Name.GetHashCode();
//Get hash code for the Code field.
int hashProductCode = product.Code.GetHashCode();
//Calculate the hash code for the product.
return hashProductName ^ hashProductCode;
}
Defining GetHashCode to return a unique answer causes the Distinct to work as expected;
public int GetHashCode(Pairing obj)
{
if (obj==null) return 0;
var hc1 = obj.Index.GetHashCode();
var hc2 = obj.Length.GetHashCode();
var hc3 = obj.Offset.GetHashCode();
return hc1 ^ hc2 ^ hc3;
}