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

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.

Related

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.

C# nth-child logic test

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.

bugs in java code after converting from C#

here is a function prints repeating int in a array.
in c#:
int [] ReturnDups(int[] a)
{
int repeats = 0;
Dictionary<int, bool> hash = new Dictionary<int>();
for(int i = 0; i < a.Length i++)
{
bool repeatSeen;
if (hash.TryGetValue(a[i], out repeatSeen))
{
if (!repeatSeen)
{
hash[a[i]] = true;
repeats ++;
}
}
else
{
hash[a[i]] = false;
}
}
int[] result = new int[repeats];
int current = 0;
if (repeats > 0)
{
foreach(KeyValuePair<int,bool> p in hash)
{
if(p.Value)
{
result[current++] = p.Key;
}
}
}
return result;
}
now converted to JAVA by Tangible software's tool.
in java:
private int[] ReturnDups(int[] a)
{
int repeats = 0;
java.util.HashMap<Integer, Boolean> hash = new java.util.HashMap<Integer>();
for (int i = 0; i < a.length i++)
{
boolean repeatSeen = false;
if (hash.containsKey(a[i]) ? (repeatSeen = hash.get(a[i])) == repeatSeen : false)
{
if (!repeatSeen)
{
hash.put(a[i], true);
repeats++;
}
}
else
{
hash.put(a[i], false);
}
}
int[] result = new int[repeats];
int current = 0;
if (repeats > 0)
{
for (java.util.Map.Entry<Integer,Boolean> p : hash.entrySet())
{
if (p.getValue())
{
result[current++] = p.getKey();
}
}
}
return result;
}
but findbug find this line of code as bugs. and it looks very odd to me too.
if (hash.containsKey(a[i]) ? (repeatSeen = hash.get(a[i])) == repeatSeen : false)
can someone pls explain to me what this line does and how do i write it in java properly?
thanks
You have overcomplicated the code for TryGetValue - this simple translation should work:
if ( hash.containsKey(a[i]) ) {
if (!hash.get(a[i])) {
hash.put(a[i], true);
}
} else {
hash.put(a[i], false);
}
C# has a way to get the value and a flag that tells you if the value has been found in a single call; Java does not have a similar API, because it lacks an ability to pass variables by reference.
Do not directly convert C# implementation. assign repeatSeen value only if the id is there.
if (hash.containsKey(a[i]))
{
repeatSeen = hash.get(a[i]).equals(repeatSeen)
if (!repeatSeen)
{
hash.put(a[i], true);
repeats++;
}
}
To answer the actual question that was asked:
if (hash.containsKey(a[i]) ? (repeatSeen = hash.get(a[i])) == repeatSeen : false)
is indeed syntactically wrong. I haven't looked at the rest of the code, but having written parsers/code-generators in my time I'm guessing it was supposed to be
if (hash.containsKey(a[i]) ? (repeatSeen = hash.get(a[i])) == repeatSeen) : false)
It's gratuitously ugly -- which often happens with code generators, especially ones without an optimizing pass -- but it's syntactically correct. Let's see if it actually does have a well-defined meaning.
CAVEAT: I haven't crosschecked this by running it -- if someone spots an error, please tell me!
First off, x?y:z is indeed a ternary operator, which Java inherited from C via C++. It's an if-then-else expression -- if x is true it has the value y, whereas if x is false it has the value z. So this one-liner means the same thing as:
boolean implied;
if (hash.containsKey(a[i]) then
implied = (repeatSeen = hash.get(a[i])) == repeatSeen);
else
implied = false;
if(implied)
... and so on.
Now, the remaining bit of ugliness is the second half of that and-expression. I don't know if you're familiar with the use of = (assignment) as an expression operator; its value as an operator is the same value being assigned to the variable. That's mostly intended to let you do things like a=b=0;, but it can also be used to set variables "in passing" in the middle of an expression. Hardcore C hackers do some very clever, and ugly, things with it (he says, being one)... and here's it's being used to get the value from the hashtable, assign it to repeatSeen, and then -- via the == -- test that same value against repeatSeen.
Now the question is, what order are the two arguments of == evaluated in? If the left side is evaluated first, the == must always be true because the assignment will occur before the right-hand side retrieves the value. If the right side is evaluated first, we'd be comparing the new value against the previous value, in an very non-obvious way.
Well, in fact, there's another StackOverflow entry which addresses that question:
What are the rules for evaluation order in Java?
According to that, the rule for Java is that the left argument of an operator is always evaluated before the right argument. So the first case applies, the == always returns true.
Rewriting our translation one more time to reflect that, it turns into
boolean implied;
if (hash.containsKey(a[i]) then
{
repeatSeen = hash.get(a[i]));
implied = true;
}
else
implied = false;
if(implied)
Which could be further rewritten as
if (hash.containsKey(a[i]) then
{
repeatSeen = hash.get(a[i]));
// and go on to do whatever else was in the body of the original if statement
"If that's what they meant, why didn't they just write it that way?" ... As I say, I've written code generators, and in many cases the easiest thing to do is just make sure all the fragments you're writing are individually correct for what they're trying to do and not worry about whether they at all resemble what a human would have written do do the same thing. In particular, it's tempting to generate code according to templates which allow for cases you may not actually use, rather than trying to recognize the simpler situation and generate code differently.
I'm guessing that the compiler was drawing in and translating bits of computation as it realized it needed them, and that this created the odd nesting as it started the if, then realized it needed a conditional assignment to repeatSeen, and for whatever reason tried to make that happen in the if's test rather than in its body. Believe me, I've seen worse kluging from code generators.

C# - Shorter version of IF statement

Is there a shorter version of IF statement to do this?
if (el.type == ElementType.Type1 || el.type == ElementType.Type2)
You could use an extension method, but would this really be much better?
Throw this on a static class:
public static bool IsOneOf(this ElementType self, params ElementType[] options)
{
return options.Contains(self);
}
And then you can do:
if (el.type.IsOneOf(ElementType.Type1, ElementType.Type2)) {
However, this will be a lot slower than your if statement, as there is an implicit array initialization followed by an array traversal, as opposed to (at the most) two compares and branches.
Consider ElementType is defined as
enum ElementType
{
Type1,
Type2,
Type3
}
In this particular case you may write if(el.type<ElementType3)
By default Type1 equals to 0, Type2 equals 1, etc
If you have only 2 values, I strongly suggest to use the code you posted, because is likely the most readable, elegant and fast code possible (IMHO).
But if you have more cases like that and more complicated, you could think to use a switch statement:
switch (el.type)
{
case ElementType.Type1:
case ElementType.Type2:
case ElementType.Type3:
//code here
break;
case ElementType.Type4:
case ElementType.Type5:
//code here
break;
case ElementType.Type6:
//code here
break;
}
that translated in if statements would be:
if (el.type == ElementType.Type1 ||
el.type == ElementType.Type2 ||
el.type == ElementType.Type3 )
{
// code here
}else if(el.type == ElementType.Type4 ||
el.type == ElementType.Type5)
{
// code here
}else if(el.type == ElementType.Type6)
{
// code here
}
They're perfectly equal to me, but the switch seems more readable/clearer, and you need to type less (i.e. it's "shorter" in term of code length) :)
You can try this:
if(new [] { ElementType.Type1, ElementType.Type2 }.Contains(el.type))
(turns out, that takes even more characters)
I guess you're referring to an IN() clause or some such? Not really... Well, sort of... You can do something like:
if ((new [] { ElementType.Type1, ElementType.Type2}).Contains(el.type)) {...}
But that's not going to be anywhere near as performant (or brief) as what you're already doing. You can also do
if (el.type == ElementType.Type1 | el.type == ElementType.Type2)
but that doesn't do short-circuit evaluation, so you rarely want to use that operator. My advice is to stick with what you have.
The brief answer is no. There isn't C# language construct that lets you combine object comparisons. But as many people have mentioned before, creating a collection of your types is probably your best bet in creating a shorter if statement. However that sacrifices quite a bit in the area of performance. I would stick with the OR statement.
There is no better way to optimize your code. As other users have shown, you can optimize an if else.
But a type of if statement I have thought about, in your case especially, would be
if(X > [Y || Z || A])
But that doesn't exist, and isn't as clean as the current if (X > Y || X > Z || X > A)
(This is more of a response to Cody Gray)
If this is a common logic comparison in your code that shows up alot I'd just write a method to handle it.
private bool isType1OrType2(ElementType type)
{
return type == ElementType.Type1 || type == ElementType.Type2;
}
then you can do
if(isType1OrType2(el.type))
You could also make this an extension method like so
public static bool isType1OrType2(this ElementType type)
{
return type == ElementType.Type1 || type == ElementType.Type2;
}
so the code would read a little nicer
if(el.type.isType1OrType2())
But then you have to have a static class but you can decide if it's worth it. I personally would not write a method to take a collection of types to compare to unless you find that you are comparing the type to many different combinations. I also would not even bother changing the code at all if this is the only place you make this type of comparison.
i dont think there is a way to optimize your statement
In short: nothing reasonable (reasonable in terms of code readability and performance optimisation). I wouldn't recommend the ternary operator for this kind of comparison either.
The actual if can be shortened to 5 characters ;)
bool b = (el.type == ElementType.Type1) | (el.type == ElementType.Type2);
if(b){...}
Don't do this, it is stupid and confusing unless you have a finite-state automaton.
enum MyEnum
{
A,
B,
C
}
private readonly Dictionary<MyEnum, Action> _handlers = new Dictionary<MyEnum, Action>
{
{MyEnum.A,()=>Console.Out.WriteLine("Foo")},
{MyEnum.B,()=>Console.Out.WriteLine("Bar")},
};
public static void ActOn(MyEnum e)
{
Action handler = null;
if (_handlers.TryGetValue(e, out handler) && handler != null)
{
handler();
}
}
Another approach would be to do some bitwise comparison, but really not worth it again.
private void ActWithCast(MyEnum e)
{
const int interest = (int)MyEnum.A | (int)MyEnum.B;
if (0 != ((int)e & interest))
{
Console.Out.WriteLine("Blam");
}
}
If the ElementType is an enum there is a shorter way to do it:
[Flags]
public enum ElementType
{
Type1 = 1,
Type2 = 2,
Type3 = 4,
}
...
tElementType.HasFlag(ElementType.Type1 | ElementType.Type2);
You do not need the [Flags] attribute to use HasFlag, but the values of each of them do need to follow that pattern.

IEqualityComparer for Value Objects

I have an immutable Value Object, IPathwayModule, whose value is defined by:
(int) Block;
(Entity) Module, identified by (string) ModuleId;
(enum) Status; and
(entity) Class, identified by (string) ClassId - which may be null.
Here's my current IEqualityComparer implementation which seems to work in a few unit tests. However, I don't think I understand what I'm doing well enough to know whether I am doing it right. A previous implementation would sometimes fail on repeated test runs.
private class StandardPathwayModuleComparer : IEqualityComparer<IPathwayModule>
{
public bool Equals(IPathwayModule x, IPathwayModule y)
{
int hx = GetHashCode(x);
int hy = GetHashCode(y);
return hx == hy;
}
public int GetHashCode(IPathwayModule obj)
{
int h;
if (obj.Class != null)
{
h = obj.Block.GetHashCode() + obj.Module.ModuleId.GetHashCode() + obj.Status.GetHashCode() + obj.Class.ClassId.GetHashCode();
}
else
{
h = obj.Block.GetHashCode() + obj.Module.ModuleId.GetHashCode() + obj.Status.GetHashCode() + "NOCLASS".GetHashCode();
}
return h;
}
}
IPathwayModule is definitely immutable and different instances with the same values should be equal and produce the same HashCode since they are used as items within HashSets.
I suppose my questions are:
Am I using the interface correctly in this case?
Are there cases where I might not see the desired behaviour?
Is there any way to improve the robustness, performance?
Are there any good practices that I am not following?
Don't do the Equals in terms of the Hash function's results it's too fragile. Rather do a field value comparison for each of the fields. Something like:
return x != null && y != null && x.Name.Equals(y.Name) && x.Type.Equals(y.Type) ...
Also, the hash functions results aren't really amenable to addition. Try using the ^ operator instead.
return obj.Name.GetHashCode() ^ obj.Type.GetHashCode() ...
You don't need the null check in GetHashCode. If that value is null, you've got bigger problems, no use trying to recover from something over which you have no control...
The only big problem is the implementation of Equals. Hash codes are not unique, you can get the same hash code for objects which are different. You should compare each field of IPathwayModule individually.
GetHashCode() can be improved a bit. You don't need to call GetHashCode() on an int. The int itself is a good hash code. The same for enum values. Your GetHashCode could be then implemented like this:
public int GetHashCode(IPathwayModule obj)
{
unchecked {
int h = obj.Block + obj.Module.ModeleId.GetHashCode() + (int) obj.Status;
if (obj.class != null)
h += obj.Class.ClassId.GetHashCode();
return h;
}
}
The 'unchecked' block is necessary because there may be overflows in the arithmetic operations.
You shouldn't use GetHashCode() as the main way of comparison objects. Compare it field-wise.
There could be multiple objects with the same hash code (this is called 'hash code collisions').
Also, be careful when add together multiple integer values, since you can easily cause an OverflowException. Use 'exclusive or' (^) to combine hashcodes or wrap code into 'unchecked' block.
You should implement better versions of Equals and GetHashCode.
For instance, the hash code of enums is simply their numerical value.
In other words, with these two enums:
public enum A { x, y, z }
public enum B { k, l, m }
Then with your implementation, the following value type:
public struct AB {
public A;
public B;
}
the following two values would be considered equal:
AB ab1 = new AB { A = A.x, B = B.m };
AB ab2 = new AB { A = A.z, B = B.k };
I'm assuming you don't want that.
Also, passing the value types as interfaces will box them, this could have performance concerns, although probably not much. You might consider making the IEqualityComparer implementation take your value types directly.
Assuming that two objects are equal because their hash code is equal is wrong. You need to compare all members individually
It is proabably better to use ^ rather than + to combine the hash codes.
If I understand you well, you'd like to hear some comments on your code. Here're my remarks:
GetHashCode should be XOR'ed together, not added. XOR (^) gives a better chance of preventing collisions
You compare hashcodes. That's good, but only do this if the underlying object overrides the GetHashCode. If not, use properties and their hashcodes and combine them.
Hash codes are important, they make a quick compare possible. But if hash codes are equal, the object can still be different. This happens rarely. But you'll need to compare the fields of your object if hash codes are equal.
You say your value types are immutable, but you reference objects (.Class), which are not immutable
Always optimize comparison by adding reference comparison as first test. References unequal, the objects are unequal, then the structs are unequal.
Point 5 depends on whether the you want the objects that you reference in your value type to return not equal when not the same reference.
EDIT: you compare many strings. The string comparison is optimized in C#. You can, as others suggested, better use == with them in your comparison. For the GetHashCode, use OR ^ as suggested by others as well.
Thanks to all who responded. I have aggregated the feedback from everyone who responded and my improved IEqualityComparer now looks like:
private class StandardPathwayModuleComparer : IEqualityComparer<IPathwayModule>
{
public bool Equals(IPathwayModule x, IPathwayModule y)
{
if (x == y) return true;
if (x == null || y == null) return false;
if ((x.Class == null) ^ (y.Class == null)) return false;
if (x.Class == null) //and implicitly y.Class == null
{
return x.Block.Equals(y.Block) && x.Status.Equals(y.Status) && x.Module.ModuleId.Equals(y.Module.ModuleId);
}
return x.Block.Equals(y.Block) && x.Status.Equals(y.Status) && x.Module.ModuleId.Equals(y.Module.ModuleId) && x.Class.ClassId.Equals(y.Class.ClassId);
}
public int GetHashCode(IPathwayModule obj)
{
unchecked {
int h = obj.Block ^ obj.Module.ModuleId.GetHashCode() ^ (int) obj.Status;
if (obj.Class != null)
{
h ^= obj.Class.ClassId.GetHashCode();
}
return h;
}
}
}

Categories