Mike
Mike

Reputation: 2339

How to compare all values in an object without repeating if statements?

I am trying to compare all possible values in a list of objects like this:

public class Object21
{
    int Id,
    bool firstbool,
    bool secondbool
}

I would loop through the objects and compare them like this:

List<Object1> objects;

foreach(var o in objects)
{
    if(firstbool && secondbool)
        ....
    if(firstbool && !secondbool)
        ....
    if(!firstbool && secondbool)
        ....
    if(!firstbool && !secondbool)
        ....
}

This seems ok, but what if the object had several values that you were running through if statements.

public class Object2
{
    int Id;
    int firstbool;
    ....
    int twentiethbool;
}

Then you would have to write out all of the possible conditional statements and your code would be terribly written and hard to read.

List<Object2> objects2;
foreach(var o in objects2)
{
     if(firstbool && secondbool && ... && twentiethbool)
         ....
     if(....)
         ....
     ....
         ....
     if(!firstbool && !secondbool && ... && !twentiethbool)
         ....
}

Is there a simpler way to write the second scenario so that you are not writing every combination of if statements?

In the end I would like to calculate the percentage occurrence of each condition in the list.

Upvotes: 0

Views: 185

Answers (5)

Dmytro
Dmytro

Reputation: 17176

If you have some unique action for each boolean properties combination I suggest you to use some kind of string key for your object, generated on those values. Something like "001001", "000000" etc. Then use Dictionary<string, Func<int>> to hold all your unique actions, get and perform the right one by it's key. For example:

public class Object21
{
    public int Id { get; set; }
    public bool FirstBool { get; set; }
    public bool SecondBool { get; set; }
    public bool ThirdBool { get; set; }
    public bool FourthBool { get; set; }
    public bool FifthBool { get; set; }
    public bool SixthBool { get; set; }

    public void Process()
    {
        // Perform the action
        Actions[Key]();
    }

    // Returns "001001" like representation of your object
    public string Key
    {
        get
        {
            return string.Join(string.Empty, GetType()
                .GetProperties(BindingFlags.Public | BindingFlags.Instance)
                .Where(x => x.PropertyType == typeof(bool))
                .Select(x => (bool)x.GetValue(this, null) ? "1" : "0" ));
        }
    }

    private static Dictionary<string, Func<int>> Actions
    {
        get
        {
            return new Dictionary<string, Func<int>>
            {
                {"000000", new Func<int>(delegate
                {
                    Console.WriteLine("000000 action performed.");
                    return 0;
                })},
                {"000001", new Func<int>(delegate
                {
                    Console.WriteLine("000001 action performed.");
                    return 1;
                })},
                {"000010", new Func<int>(delegate
                {
                    Console.WriteLine("000010 action performed.");
                    return 2;
                })},

                // More actions

                {"111111", new Func<int>(delegate
                {
                    Console.WriteLine("111111 action performed.");
                    return 63;
                })}
            };
        }
    }
}

And then use this in your program like this:

static void Main(string[] args)
{
    var list = new List<Object21>
    {
       // initialize your list
    };

    foreach (var object21 in list)
    {
        object21.Process();
    }

    // Calculate your occurrences (basically what @Grant Winney suggested)
    var occurrences = list.GroupBy(o => o.Key).ToDictionary(g => g.Key, g => (g.Count() / (double)list.Count)*100);

    foreach (var occurrence in occurrences)
    {
        Console.WriteLine("{0}: {1}%", occurrence.Key, occurrence.Value);
    }
}

Upvotes: 0

totoro
totoro

Reputation: 3407

it's probably easier to rewrite your class, storing each condition in an array like follows:

public class MyObject
{
    public static int numFields = 20;
    public enum Conditions
    {
        C1, C2, C3, .... C20 //name for each condition, so can set values using descriptive names
    };

    public Boolean[] BinaryFields = new Boolean[numFields];

    public void setCondition(Conditions condition, Boolean value)
    {
        BinaryFields[(int)condition] = value;
    }

    public override string ToString()
    {
        return string.Join(",", BinaryFields);
    }
}

then you can calculate the stat by counting what is actually there, instead of numerating through all of the 2^20 possibilities. something like follows:

static void Main(string[] args)
{
    //simulation: creat 10 MyObjects
    List<MyObject> lst = new List<MyObject>();
    for (int i = 0; i < 10; i++)
    {
        MyObject m = new MyObject();
        //example of setting condition
        m.setCondition(MyObject.Conditions.C1, true);
        lst.Add(m);
    }

    //calculate stat
    var resultCount = new Dictionary<string, int>(); //conditionResult, count
    foreach (MyObject m in lst)
    {
        if (resultCount.ContainsKey(m.ToString()))
        {
            resultCount[m.ToString()] += 1;
        }
        else
        {
            resultCount.Add(m.ToString(), 1);
        }
    }

    //print stat
    foreach(KeyValuePair<string, int> entry in resultCount){
        Debug.WriteLine("probability for conditoin={0} is {1}", entry.Key, (double)entry.Value / lst.Count);
    }
}

Upvotes: 0

Grant Winney
Grant Winney

Reputation: 66439

Given a class structure like this:

public class YourClass
{
    public int Id { get; set; }
    public bool firstbool { get; set; }
    public bool secondbool { get; set; }
    public bool thirdbool { get; set; }
}

You can use reflection to get all the boolean values (and only bool values) inside the class:

public IEnumerable<bool> GetBools(YourClass obj)
{
    return obj.GetType()
              .GetProperties(BindingFlags.Public | BindingFlags.Instance)
              .Where(x => x.PropertyType == typeof (bool))
              .Select(x => (bool)x.GetValue(obj, null));
}

Then use LINQ to iterate through the collection, and create a dictionary of combinations and totals:

List<YourClass> objects = new List<YourClass>();

var totals = objects.GroupBy(x => String.Join(",", GetBools(x)))
                    .ToDictionary(x => x.Key, x => x.Count() / (double)objects.Count);

This will give you a dictionary with each unique combination and the percentage it occurs.

Given this input:

var o = new List<YourClass>
        {
            new YourClass {firstbool = true, secondbool = true, thirdbool = false},
            new YourClass {firstbool = false, secondbool = false, thirdbool = false},
            new YourClass {firstbool = true, secondbool = true, thirdbool = false}
        };

The result in the dictionary will be:

{["True,True,False", 0.666666666666667]}

{["False,False,False", 0.333333333333333]}

Upvotes: 0

BradleyDotNET
BradleyDotNET

Reputation: 61349

To answer the first part of the question (about comparing every combination):

There isn't really a good way to do that, other than write a bunch of if statements. Of course; you probably shouldn't be doing that anyways :)

You could probably use reflection and recursion, but thats going to get messy really fast.

Luckily, to just get the percentage occurrence of each flag, you can just do:

list.Count(i => i.firstbool) / (double)list.Count();
...

Upvotes: 3

feiyun0112
feiyun0112

Reputation: 771

first, create a dictionary to save all conditions

var dict = new Dictionary<string, int>{{"001",0},{"010",0} ...}

then, create key use bool values

  var key=string.Empty;
  key+=firstbool ?"0":"1"
  key+=secondbool ?"0":"1"
  ......

after all, you can know which condition occurred

dict[key]++;

Upvotes: 0

Related Questions