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;
}
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 have a c# class
public class treeItem
{
public string parentName { get; set; }
public int index { get; set; }
public List<treeItem> children { get; set; }
public treeItem()
{
children = new List<treeItem>();
}
}
i can fill it and sort it by index but i have a problem in search of index in 3rd or n level
function to add index
void AddIndexToTree(List<treeItem> children)
{
for(int i = 0; i < children.Count; i++)
{
count++;
children[i].index = count;
if (children[i].children.Count > 0)
{
AddIndexToTree(children[i].children);
}
}
}
i try to create a search function but have a problem in n level that i remove item from list so i send a copy of list to function but in n level it delete from the original list
treeItem getParentNode(int index, List<treeItem> searchList)
{
if (searchList.Count == 1)
{
if (searchList[0].index == index)
{
return searchList[0];
}
else if (searchList[0].children.Count > 0)
{
for (int i = 0; i < searchList[0].children.Count; i++ )
{
if (searchList[0].children[i].index == index)
return searchList[0].children[i];
}
List<treeItem> tempList = searchList[0].children;
return getParentNode(index, tempList);
}
}
else
{
int length = searchList.Count;
int mid;
if( length % 2 == 0)
{
mid = length / 2;
if (length != 2)
mid--;
}
else
{
mid = length / 2;
}
if (searchList[mid].index == index)
return searchList[mid];
if(searchList[mid].index > index)
{
searchList.RemoveRange(mid, searchList.Count - mid );
return getParentNode(index, searchList);
}
else
{
if (searchList.Count > 2 && searchList[mid + 1].index < index )
searchList.RemoveRange(0, mid + 1);
else
searchList.RemoveRange(0, mid);
return getParentNode(index, searchList);
}
}
return null;
}
any help in my code or another algorithm please ?
Here is an example of a simple recursive search for a specific index in a tree made up from your class: (of course, this should be a method in your class)
public treeItem Find(int index)
{
if(this.Index == index)
return this;
foreach(var child in this.children)
return child.Find(index);
return null; // Index was not found in the current treeItem or any of it's children.
}
Please note that if the treeItems in your children property are ordered by index, you can use a binary search (which is what I think you tried to do).
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;
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 to test the equality of trees. In other other words objects which contains List<T> with childs and the childs also contains List<T> with childs and so on.
I've found that you can test List with CollectionAssert, however it does not work that well with composites.
Any suggestions? MSUnit is my test library.
Example
IReagentComposed bronzeBarParsed = (from n in composedCrafts where n.ItemId == 2841 select n).Single();
IReagentComposed bronzeBar = new Craft()
{
ItemId = 2841,
Profession = Profession.Mining,
Quantity = 0,
QuantityCrafted = 0,
Skill = 50,
Reagents = new List()
{
new Craft()
{
ItemId = 2840,
Quantity = 0,
Skill = 1,
Profession = Profession.Mining,
Reagents = new List()
{
new Reagent()
{
ItemId = 2770,
Quantity = 1
}
}
},
new Craft()
{
ItemId = 3576,
Quantity = 0,
Skill = 50,
Profession = Profession.Mining,
Reagents = new List()
{
new Reagent()
{
ItemId = 2771,
Quantity = 1
}
}
}
}
};
Assert.AreEqual(bronzeBar, bronzeBarParsed);
Craft and Reagent
public class Craft : IReagentComposed
{
public int QuantityCrafted { get; set; }
public int Quantity { get; set;}
public int ItemId { get; set; }
public int Skill { get; set; }
public Profession Profession { get; set; }
public IEnumerable Reagents { get; set; }
public override bool Equals(object other)
{
if (other == null || GetType() != other.GetType()) return false;
IReagentComposed o = other as IReagentComposed;
return o != null && this.Quantity == o.Quantity &&
this.ItemId == o.ItemId &&
this.Profession == o.Profession &&
this.Reagents == o.Reagents && //also tried Equals
this.Skill == o.Skill;
}
public override int GetHashCode()
{
return 0;
}
}
public class Reagent : IReagent
{
public int ItemId { get; set; }
public int Quantity { get; set; }
public override bool Equals(object other)
{
if (other == null || GetType() != other.GetType()) return false;
IReagent o = other as IReagent;
return o != null && o.ItemId == this.ItemId && o.Quantity == this.Quantity;
}
public override int GetHashCode()
{
return 0;
}
}
return o != null && this.Quantity == o.Quantity &&
this.ItemId == o.ItemId &&
this.Profession == o.Profession &&
this.Reagents == o.Reagents && //also tried Equals
this.Skill == o.Skill;
The Reagents are unlikely to match, they are distinct List objects. List<> doesn't override Equals although it isn't that clear what actual type you use. Write a little helper function that takes two lists of reagents and checks for equality. You'd typically start at comparing List<>.Count and then work down the elements one by one.
Added a extension method for IEnumberable<T>:
public static class IEnumberableExtensions
{
public static bool AreEnumerablesEqual<T>(this IEnumerable<T> x, IEnumerable<T> y)
{
if (x.Count() != y.Count()) return false;
bool equals = false;
foreach (var a in x)
{
foreach (var b in y)
{
if (a.Equals(b))
{
equals = true;
break;
}
}
if (!equals)
{
return false;
}
equals = false;
}
return true;
}
}