Suppose that you are creating a game in which your character obtains some awards out of some predefined one, as the game goes by. Or, you’re creating software in which you want your user to select some entries/items (imagine something like a checked list box). In both cases, you could try and use these two approaches
- Create or use a data structure (e.g. Dictionary<K,V>) in which you store a reference to all possible items along with the relevant Boolean value. Easy right? In order to answer the question if user has obtained item X, you just check the value of the relevant key in the dictionary.
- Create or use a data structure (e.g. List<T>) in which you store references to the items the user has obtained/picked. So, you can use the List.Contains method to check for an item’s existence.
Both described methods are efficient, easy to grasp and use. However, there is another method in which you do not need to use nothing more advanced than a simple integer. We’ll be using binary system arithmetic and logic to accomplish this purpose.
Each number can be written in binary format in a sequence of 1s and 0s. For instance, 15 is binary 1111 and 2 is binary 0010. Most importantly, all numbers that are a power of two start with 1 and finish with some 0s. Check the below list
Binary – Base 10
1-1
10-2
100-4
1000-8
10000-16
100000-32
1000000-64
…
So, if you assign e.g. “valueA”to 1, “valueB” to 2, “valueC” to 4 etc., you can easily assign multiple values into an integer. How? You just add the relevant base 10 value to this integer, let’s call it storage. For instance, the integer 13 contains 3 values: a)1, b)4, c)8. In binary form, it is written as 1101. The way to check for a value’s existence is a logical bitwise AND between the storage and the respective value. In C#, we use the & for this purpose. If the result of the operation is equal to the value, then storage contains this value. Simple, right? Check the below C# code for the value 19.
int storage = 19; //10011 int a = 1; //1 int b = 2; //10 int c = 4; //100 int d = 8;//1000 int e = 16;//10000 Console.WriteLine("--------------------------------"); Console.WriteLine("Check for the value 19 - 10011"); Console.WriteLine($"Does {storage} contain \"a\" with the value of 1? " + ((storage & a) == a)); //true Console.WriteLine($"Does {storage} contain \"b\" with the value of 2? " + ((storage & b) == b)); //true Console.WriteLine($"Does {storage} contain \"c\" with the value of 4? " + ((storage & c) == c)); //false Console.WriteLine($"Does {storage} contain \"d\" with the value of 6? " + ((storage & d) == d)); //false Console.WriteLine($"Does {storage} contain \"e\" with the value of 8? " + ((storage & e) == e)); //true
Another way to understand/remember it is that each digit in the number’s binary form is something like a flag. For example, if you count digits starting from the last one (and begin counting from zero) and the 4th position has the value 1, then the number contains the value 2^3 (=8).
The only drawback using this method is that you cannot use it for more than 32 items (for a System.Int32 .NET type, since its maximum value is 2^32 – 1). So, how about a more concrete example? Let’s suppose that we want to check if a user has selected some cities. First, we need to create a 1:1 relationship between each city and a number, which will of course be a power of two.
Dictionary<string,int> dict = new Dictionary<string,int> (); dict.Add("Athens", (int)Math.Pow(2, 0)); //1 dict.Add("Thessaloniki", (int)Math.Pow(2, 1)); //2 dict.Add("Kozani", (int)Math.Pow(2, 2)); //4 dict.Add("Larisa", (int)Math.Pow(2, 3)); //8 dict.Add("Komotini", (int)Math.Pow(2, 4)); //16 dict.Add("Trikala", (int)Math.Pow(2, 5)); //32 dict.Add("Patra", (int)Math.Pow(2, 6)); //64
Then, we’ll let the user pick some cities, which will be added to an integer. A simple addition and we’re done.
int myStorage = dict["Athens"] + dict["Komotini"] + dict["Larisa"];
Let’s check some cities manually.
//does it contain Komotini? Console.WriteLine("Does it contain Komotini? " + ((myStorage & dict["Komotini"]) == dict["Komotini"])); //true //does it contain Larisa? Console.WriteLine("Does it contain Larisa? " + ((myStorage & dict["Larisa"]) == dict["Larisa"])); //true //does it contain Patra? Console.WriteLine("Does it contain Patra? " + ((myStorage & dict["Patra"]) == dict["Patra"])); //false
And a check on every city
foreach (var item in dict) { if ((myStorage & item.Value) != 0) //check every city Console.WriteLine("It does contain {item.Key}"); else Console.WriteLine("It does not contain {item.Key}"); }
If you need to remove a value, just subtract it from the storage.
Console.WriteLine("Let's remove Larisa"); //remove Larisa myStorage = myStorage - dict["Larisa"]; //does it contain Larisa? Console.WriteLine("Now, does it contain Larisa? " + ((myStorage & dict["Larisa"]) == dict["Larisa"]));
I’ve used this trick into an app I’ve co-developed, called Math Puppy. In this app, the child gets awarded with various accessories for his/her dog as the game progresses. I wanted something simple to store the awarded stuff, so I used this method. Nice and easy!
Update (7/1/2016): As my friend Nikolaos Georgiou pointed out to me on Twitter, you can use Enum with [Flags] attribute if you have a few values that are known in compile time. This wouldn’t work, however, if the values are dynamic e.g. from a database.
The entire sample source code is listed below. Happy coding!
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
static void Main(string[] args) | |
{ | |
int storage = 19; //10011 | |
int a = 1; //1 | |
int b = 2; //10 | |
int c = 4; //100 | |
int d = 8;//1000 | |
int e = 16;//10000 | |
Console.WriteLine("——————————–"); | |
Console.WriteLine("Check for the value 19 – 10011"); | |
Console.WriteLine($"Does {storage} contain \"a\" with the value of 1? " + ((storage & a) == a)); //true | |
Console.WriteLine($"Does {storage} contain \"b\" with the value of 2? " + ((storage & b) == b)); //true | |
Console.WriteLine($"Does {storage} contain \"c\" with the value of 4? " + ((storage & c) == c)); //false | |
Console.WriteLine($"Does {storage} contain \"d\" with the value of 6? " + ((storage & d) == d)); //false | |
Console.WriteLine($"Does {storage} contain \"e\" with the value of 8? " + ((storage & e) == e)); //true | |
//using multiples of 2 | |
Dictionary<string, int> dict = new Dictionary<string, int>(); | |
dict.Add("Athens", (int)Math.Pow(2, 0)); //1 | |
dict.Add("Thessaloniki", (int)Math.Pow(2, 1)); //2 | |
dict.Add("Kozani", (int)Math.Pow(2, 2)); //4 | |
dict.Add("Larisa", (int)Math.Pow(2, 3)); //8 | |
dict.Add("Komotini", (int)Math.Pow(2, 4)); //16 | |
dict.Add("Trikala", (int)Math.Pow(2, 5)); //32 | |
dict.Add("Patra", (int)Math.Pow(2, 6)); //64 | |
int myStorage = dict["Athens"] + dict["Komotini"] + dict["Larisa"]; | |
Console.WriteLine("——————————–"); | |
Console.WriteLine("Let's check some cities"); | |
//does it contain Komotini? | |
Console.WriteLine("Does it contain Komotini? " + ((myStorage & dict["Komotini"]) == dict["Komotini"])); //true | |
//does it contain Larisa? | |
Console.WriteLine("Does it contain Larisa? " + ((myStorage & dict["Larisa"]) == dict["Larisa"]));//true | |
//does it contain Patra? | |
Console.WriteLine("Does it contain Patra? " + ((myStorage & dict["Patra"]) == dict["Patra"]));//false | |
Console.WriteLine("——————————–"); | |
Console.WriteLine("Let's check *all* cities"); | |
foreach (var item in dict) | |
{ | |
if ((myStorage & item.Value) != 0) //check every city | |
Console.WriteLine($"It does contain {item.Key}"); | |
else | |
Console.WriteLine($"It does not contain {item.Key}"); | |
} | |
Console.WriteLine("——————————–"); | |
Console.WriteLine("Let's remove Larisa"); | |
//remove Larisa | |
myStorage = myStorage – dict["Larisa"]; | |
//does it contain Larisa? | |
Console.WriteLine("Now, does it contain Larisa? " + ((myStorage & dict["Larisa"]) == dict["Larisa"])); | |
Console.ReadLine(); | |
} |