C# nth-child logic test - c#

I've been working on my own, headless browser implementation and I feel like I am making a mess of my nth-child selector logic. Given an element and it's 0-based position in its group of siblings is there a simple, one-line expression to see if that element belongs in the result set?
public bool Evaluate(HTMLElement element)
{
if (element.parentNode == element.ownerDocument)
return false;
List<Element> children = element.Parent.Children
.Where(e => e is Element)
.Cast<Element>()
.ToList();
int index = children.IndexOf(element);
bool result = (an + b test here);
return result;
}
Currently I have a convoluted set of branching logic based on tests for 0 values for (a) and (b) and I suspect I am making it more complicated than it needs to be.

If I'm understanding correctly, you need to determine whether an n exists such that index = a*n + b for some fixed a, b.
bool result = (a == 0) ? b == index : (Math.Abs(index - b) % Math.Abs(a)) == 0;
If a is 0, then index must be b. Otherwise, a must evenly divide the difference between i and b.
Naturally, if a negative value for a is not allowed you can skip the Math.Abs(a) call.

Related

SortedSet with element duplication - can't remove element

I'm working on an implementation of the A-star algorithm in C# in Unity.
I need to evaluate a collection of Node :
class Node
{
public Cell cell;
public Node previous;
public int f;
public int h;
public Node(Cell cell, Node previous = null, int f = 0, int h = 0)
{
this.cell = cell;
this.previous = previous;
this.f = f;
this.h = h;
}
}
I have a SortedSet which allows me to store several Node, sorted by h property. Though, I need to be able to store two nodes with the same h property. So I've implemented a specific IComparer, in a way that allow me sorting by h property, and triggerring equality only when two nodes are representing the exact same cell.
class ByHCost : IComparer<Node>
{
public int Compare(Node n1, Node n2)
{
int result = n1.h.CompareTo(n2.h);
result = (result == 0) ? 1 : result;
result = (n1.cell == n2.cell) ? 0 : result;
return result;
}
}
My problem : I have a hard time to remove things from my SortedSet (I named it openSet).Here is an example:
At some point in the algorithm, I need to remove a node from the list based on some criteria (NB: I use isCell127 variable to focus my debug on an unique cell)
int removedNodesNb = openSet.RemoveWhere((Node n) => {
bool isSame = n.cell == candidateNode.cell;
bool hasWorseCost = n.f > candidateNode.f;
if(isCell127)
{
Debug.Log(isSame && hasWorseCost); // the predicate match exactly one time and debug.log return true
}
return isSame && hasWorseCost;
});
if(isCell127)
{
Debug.Log($"removed {removedNodesNb}"); // 0 nodes where removed
}
Here, the removeWhere method seems to find a match, but doesn't remove the node.
I tried another way :
Node worseNode = openSet.SingleOrDefault(n => {
bool isSame = n.cell == candidateNode.cell;
bool hasWorseCost = n.f > candidateNode.f;
return isSame && hasWorseCost;
});
if(isCell127)
{
Debug.Log($"does worseNode exists ? {worseNode != null}"); // Debug returns true, it does exist.
}
if(worseNode != null)
{
if(isCell127)
{
Debug.Log($"openSet length {openSet.Count}"); // 10
}
openSet.Remove(worseNode);
if(isCell127)
{
Debug.Log($"openSet length {openSet.Count}"); // 10 - It should have been 9.
}
}
I think the problem is related to my pretty unusual IComparer, but I can't figure whats exatcly the problem.
Also, I would like to know if there is a significative performance improvment about using an auto SortedSet instead of a manually sorted List, especially in the A-star algorithm use case.
If i write your test you do:
n1.h < n2.h
n1.cell = n2.cell -> final result = 0
n1.h > n2.h
n1.cell = n2.cell -> final result = 0
n1.h = n2.h
n1.cell != n2.cell -> final result = 1
n1.h < n2.h
n1.cell != n2.cell -> final result = -1
n1.h > n2.h
n1.cell != n2.cell -> final result = 1
when you have equality on h value (test number 3) you choose to have always the same result -> 1. so its no good you have to have another test on cell to clarify the position bacause there is a confusion with other test which gives the same result (test number 5)
So i could test with sample, but i am pretty sure you break the Sort.
So if you clarify the test, i suggest you to use Linq with a list...its best performance.
I'll answer my own topic because I've a pretty complete one.
Comparison
The comparison of the IComparer interface needs to follow some rules. Like #frenchy said, my own comparison was broken. Here are math fundamentals of a comparison I totally forgot (I found them here):
1) A.CompareTo(A) must return zero.
2) If A.CompareTo(B) returns zero, then B.CompareTo(A) must return zero.
3) If A.CompareTo(B) returns zero and B.CompareTo(C) returns zero, then A.CompareTo(C) must return zero.
4) If A.CompareTo(B) returns a value other than zero, then B.CompareTo(A) must return a value of the opposite sign.
5) If A.CompareTo(B) returns a value x not equal to zero, and B.CompareTo(C) returns a value y of the same sign as x, then A.CompareTo(C) must return a value of the same sign as x and y.
6) By definition, any object compares greater than (or follows) null, and two null references compare equal to each other.
In my case, rule 4) - symetry - was broken.
I needed to store multiple node with the same h property, but also to sort by that h property. So, I needed to avoid equality when h property are the same.
What I decided to do, instead of a default value when h comparison lead to 0 (which broke 4th rule), is refine the comparison in a way that never lead to 0 with a unique value foreach node instance. Well, this implementation is probably not the best, maybe there is something better to do for a unique value, but here is what I did.
private class Node
{
private static int globalIncrement = 0;
public Cell cell;
public Node previous;
public int f;
public int h;
public int uid;
public Node(Cell cell, Node previous = null, int f = 0, int h = 0)
{
Node.globalIncrement++;
this.cell = cell;
this.previous = previous;
this.f = f;
this.h = h;
this.uid = Node.globalIncrement;
}
}
private class ByHCost : IComparer<Node>
{
public int Compare(Node n1, Node n2)
{
if(n1.cell == n2.cell)
{
return 0;
}
int result = n1.h.CompareTo(n2.h);
result = (result == 0) ? n1.uid.CompareTo(n2.uid) : result; // Here is the additional comparison which never lead to 0. Depending on use case and number of object, it would be better to use another system of unique values.
return result;
}
}
RemoveWhere method
RemoveWhere use a predicate to look into the collection so I didn't think it cares about comparison. But RemoveWhere use internally Remove method, which do care about the comparison. So, even if the RemoveWhere have found one element, if your comparison is inconstent, it will silently pass its way. That's a pretty weird implementation, no ?

Replacing CompareTo with LINQ for array elements

I am working on a project which uses posts to represent a fence. Each fence has exactly two posts that implement IComparable and are ordered in each fence. In order to override my CompareTo on Fence, I need to compare post 0 between this and the other fence; if that result returns 0, then I need to compare post 1 between this and the other fence. I wrote a simple for loop to perform this logic, which I've included below. However, Resharper is giving me a warning that I should replace the for loop with LINQ. Is there an easy way to replace the for loop with LINQ?
public int CompareTo(Fence other)
{
for(int i = 0; i < Posts.Length; i++)
{
int c = Posts[i].CompareTo(other.Posts[i]);
if (c != 0)
return c;
}
return 0;
}
Since a Fence has exactly two Posts, then this can be reduced to :
public int CompareTo(Fence other)
{
int c = Post[0].CompareTo(other.Post[0]);
if (c == 0)
c = Post[1].CompareTo(other.Post[1]);
return c;
}
Note that you can (and probably should) replace the Post array with Post0 and `Post1'.
Note, that this could give you a completely different ordering than:
int c = Post[1].CompareTo(other.Post[1]);
if (c == 0)
c = Post[0].CompareTo(other.Post[0]);
which, presumably, is just as valid. (i.e, if this Post[0] is less than the other's, but it's Post[1] is greater, is the Fence greater or less than the other?)
If ReSharper suggests it you may easily hit AltEnterEnter and see what happens. I guess something like:
public int CompareTo(Fence other)
{
return Posts.Select((p, i) => p.CompareTo(other.Posts[i]))
.FirstOrDefault(c => c != 0);
}
This projects each Post to it's comparison result to the respective Post of the other Fence (p is the Post loop variable, i is the index). FirstOrDefault looks for the first non-zero comparsion result or returns 0 if all results are 0.
So this does exactly what your loop does (note that LINQ uses deferred execution, so when the first non-zero comparison occures, no further Posts are compared).
Note that this code is error-prone, as juharr commented: you should first null-check other and check if the two Post arrays have the same length.
(I guess that Posts is not null and the arrays don't contain null elements should be ensured by your classes' implementations).

HashSet with complex equality

Consider the following class
public class X
{
//Unique per set / never null
public ulong A { get; set; }
//Unique per set / never null
public string B { get; set; }
//Combination of C and D is Unique per set / both never null
public string C { get; set; }
public string D { get; set; }
public override bool Equals(object obj)
{
var x = (X)obj;
if (A == x.A || B==x.B)
return true;
if (C+D==x.C+x.D)
return true;
return false;
}
public override int GetHashCode()
{
return 0;
}
}
I can't think of writing a hash function in which the combination of comments over the properties above apply, just like in the Equals function, in that case is my best bet returning a 0 from the GetHashCode or am I missing something?
This is not possible. This is fundamental problem. In fact it is possible, but it is VERY hard problem to solve.
Explanation
Just think about it in reverse, in which cases your objects are NOT equal? From code I can see what they are equal by this expression:
return A == x.A || B==x.B || (C+D)==(x.C+x.D)
And not equal expression:
return A!=x.A && B!=x.B && (C+D)!=(x.C+x.D)
So your hash should be same for any particular value in equality expression and same for any particular value in not equality expression. Values can vary to infinity.
The only real possible solution for both expressions is constant value. But this solution is not optional in performance cause it will just evaporate every meaning of GetHashCode override.
Consider using IEqualityComperer interface, and equality alghorithms for task you are solving.
I think best solution to find equal objects is Indexing. You can see for example how databases are made, and how they use bit-indexing.
Why hashes is so cruel?
If it were possible, all databases in the world would easily hash everything in single hash table, and all problems with fast access will be solved.
For example, imagine your object not as object with properties but as entire object state (for example 32 boolean properties can be represented as integer).
Hash function calculates hash based on this state, but in your case you explicitely tell that some states from it's space is actually equal:
class X
{
bool A;
bool B;
}
Your space is:
A B
false false -> 0
false true -> 1
true false -> 2
true true -> 3
If you define equality like this:
bool Equal(X x) { return x.A == A || x.B == B; }
You basicaly define this state equality:
0 == 0
0 == 1
0 == 2
0 != 3
1 == 0
1 == 1
1 != 2
1 == 3
2 == 0
2 != 1
2 == 2
2 == 3
3 != 0
3 == 1
3 == 2
3 == 3
This sets should have same hash: {0,1,2} {0,1,3} {0,2,3} {1,2,3}
So, all your sets should be EQUAL in hash. This concludes that this is impossible to create Hash function better than constant value.
In this case, I would say that the hash code that defines an object as unique (i.e. overriding GetHashCode) shouldn't be the one used for your specific HashSet.
In other words, you should consider two instances of your class equal if their properties are all equal (not if any of the properties match). But then, if you want to group them by a certain criteria, use a specific implementation of IEqualityComparer<X>.
Also, strongly consider making the class immutable.
Apart from that, the only hash code I believe will really will work is constant. Anything trying to be smarter than that will fail:
// if any of the properties match, consider the class equal
public class AnyPropertyEqualityComparer : IEqualityComparer<X>
{
public bool Equals(X x, X y)
{
if (object.ReferenceEquals(x, y))
return true;
if (object.ReferenceEquals(y, null) ||
object.ReferenceEquals(x, null))
return false;
return (x.A == y.A ||
x.B == y.B ||
(x.C + x.D) == (y.C + y.D));
}
public int GetHashCode(X x)
{
return 42;
}
}
Since you will have to evaluate all properties in any case, a HashSet will not help much in this case and you might as well use a plain List<T> (in which case insertion of a list of items into a "hashset" will degrade to O(n*n).
You could consider creating an anonymous type and then returning the hashcode from that:
public override int GetHashCode()
{
// Check that an existing code hasn't already been returned
return new { A, B, C + D }.GetHashCode();
}
Make sure you create some automated tests to verify that objects with the same values return the same hashcode.
Bear in mind that once the hashcode is given out, you must continue to return that code and not a new one.

Why is my OrderBy running forever with this comparator?

I have a class,
public class NullsAreLast : IComparer<int?>
{
public int Compare (int? x, int? y)
{
if(y == null)
return -1;
else if(x == null)
return 1;
else
return (int)x - (int)y;
}
}
which is self-explanatory on how it is supposed to work.
Whenever I run
arr.OrderBy(i => i, new NullsAreLast())
with at least two null values in arr it runs forever! Any idea why?
Keep in mind that a sorting algorithm may compare the same two values several times over the process of ordering the whole sequence. Because of this, it's very important to be aware of all three possible results: less than, greater than, and equal.
This is (mostly) fine for your integer comparison at the end (the subtraction operation). There are some weird/rare edge cases when working with floating point numbers instead of integers, and calling .CompareTo() is the preferred practice anyway, but subtraction is usually good enough in this case. However, the null checks here are a real problem.
Think about what happens as a list is nearly finished sorting. You have two null values that have both made their way to the front of the list; the algorithm just needs to verify they are in the correct position. Because both x and y are null, your function should return 0. They are equivalent (for this purpose, at least). Instead, the code always returns -1. The y value will always be less than then x value, and so the algorithm will always believe it still needs to swap them. It swaps, and tries to do the same thing again. And again. And again. And again. It can never finish.
Try this instead:
public class NullsAreLast : IComparer<int?>
{
public int Compare (int? x, int? y)
{
if(!y.HasValue)
{
if (!x.HasValue) return 0;
return -1;
}
if(!x.HasValue) return 1;
return x.Value.CompareTo(y.Value);
}
}
The minus operation at the end of your Compare method isn't appropriate for comparison. You need to handle exactly three possibilities - x is bigger, y is bigger, or they are the same.
MSDN
Compares two objects and returns a value indicating whether one is
less than, equal to, or greater than the other.
With this code, suppose X was 1000 and Y was 15. Your result would be 985, which doesn't make sense here.
Given your code and method name, I'm going to guess what you meant is this:
public class NullsAreLast : IComparer<int?>
{
public int Compare (int? x, int? y)
{
if(y == null)
return -1;
else if(x == null)
return 1;
else{
int diff = x - y;
if (diff == 0) return 0; //same
if (diff < 0) return 1; //y was bigger
if (diff > 0) return -1; //x was bigger
}
}
}
You could even smash it into a horrible one-liner:
return (y==null?-1:(x==null?1:(x-y==0?0:(x-y<0?1:-1))));

Best way to check multiple boolean conditions in C# if statements

I have 3 booleans on my code (C#) and an int32 property that depends on what booleans are true and false.
Whats the best way to accomplish this in another way than if statements like:
if(a && b && !c)
d = 1;
if(a && !b && !c)
d = 2;
//etc.. ect...
EDIT: The 3 booleans must have every combination possible to set the int32 value.
EDIT 2: The value of "d" can be the same for two different boolean comparations.
It is better to capture the intent of the operation instead of explicitly check the boolean values.
For example:
public void Check()
{
if (HasOrdered())
{
// do logic
}
}
private bool HasOrdered()
{
return a && !b && !c;
}
private bool HasBooked()
{
return a && b && !c;
}
You could use a Karnaugh map to reduce your equations and have fewer ifs.
https://en.wikipedia.org/wiki/Karnaugh_map
I think what your doing now is perfectly fine and any other solutions would be down to preference.
My preference, where it applies would be to separate the checks out if possible.
if (!a)
return;
if (!b)
return;
if (!c)
return;
This would be useful in the event that you need to check certain prereqs before issuing a function, like if the user has logged in, if a parameter exists and is in the right context, along with other items.
Like i said this might not apply but i just wanted to voice my opinion
You could do the lookup table hint given by #Adriano, assuming you have lookup_table filled with values for index [0..8):
var index = new [] { a,b,c }.Aggregate(0, (a,i) => return 2*a + (i?1:0));
int d = lookup_table[index];
Edit The EDIT of the question made this irrelevant: What does d mean?
If it's the count of false values (possible from the sample code), make it
int d = new [] { a,b,c }.Count(b => !b);
I don't see anything wrong with how you're doing it, but if the output is the same for multiple conditions you may be able to simplify if by creating a truth table and simplifying the conditions.
For example, if d should be 0 anytime a is false you could simplify to:
if(a)
if(b && !c)
d = 1;
if(!b && !c)
d = 2;
...
else
d = 0;
Or if there is some mathematical pattern (e.g. a, b, and c represent the three digits of a binary number) then you could do bit arithmetic.
If, however, you have 8 distinct outcomes (one for each combination of a, b, and c) then your method is fine.

Categories