5

I have an object with multiple boolean (and integer) properties. I want to create a collection of objects with lot of different properties to have a good dataSet used for tests.

For example

public class A
{
     public int value {get; set;} //want to try beetween 0 and 5
     public bool foo {get; set;}
     public bool bar {get; set;}
     public bool boo {get; set;}
}

For this example I like to have a list with 6*2*2 = 24 elementw, with value = 0/1/2/3/4/5, foo = true/false, bar = true/false, but boo always false.

I could do that using 24 lines of code. But it's a lot and if I have one more bool it doubles the number of line.

I may be use one loop per property but I don't think it's a good way to have too many nested loop. I also might do this by reflection, but I think this is too much work, isn´t it?

I think there is another way to do it better but I don't find it.

MakePeaceGreatAgain
  • 35,491
  • 6
  • 60
  • 111
dufaux
  • 681
  • 2
  • 8
  • 18
  • 2
    "I don't think it's a good way to have too many nested loop." why not, seems a perfectly reasonable thing to do here... – tolanj Mar 07 '16 at 10:17
  • have a look at Linq Cartesian Product examples, should be quite easy and nice to build – Mike Miller Mar 07 '16 at 10:42
  • Possible duplicate of [Generating all Possible Combinations](http://stackoverflow.com/questions/3093622/generating-all-possible-combinations) – Mike Miller Mar 07 '16 at 10:44

3 Answers3

3

What is your problem on this old loop-based approach?

var boolVals = new[] { true, false };
var intVals = new[] { 0, 1, 2, 3, 4, 5 };

var myList = new List<A>();

foreach(var foo in boolVals) {
    foreach(var value in intVals) {
        foreach(var bar in boolVals) {
            foreach (var boo in boolVals) {
                myList.Add(new A { value = value, foo = foo, bar = bar, boo = boo });
            }
        }
    }
}

You have to add a new nesting for every property you add and loop the appropriate set of values this property might have.

This appraoch fils every possible permutation of your values within a few lines of easy to read code.

Of course it looks a bit ugly. But it increases only by one further nesting on every added property.

MakePeaceGreatAgain
  • 35,491
  • 6
  • 60
  • 111
1

Since you don't explicitly ask for an all-permutation solution, I'd suggest going random. You could employ the constructor...

public class A
{
  public int value {get; set;} //want to try beetween 0 and 5
  public bool foo {get; set;}
  public bool bar {get; set;}
  public bool boo {get; set;}

  public A()
  {
     // initialize value, foo, bar, boo with desired default / random here

  }
}

var myList = new List<A>();
for (int i=0; i<=24; i++) myLyst.add(new A());

this seems quite readable and maintainable to me. You could also pass an argument to new A() and use it to calculate the n-th permutation if needed, but I guess this goes beyond the scope of the question.

Leo128
  • 163
  • 12
0

You can do this using reflection and a data structure that keeps track of the value (via equivalent indices) for each property

This automatically accounts for all properties in the class and you can extend it for other property types by simply adding to the ValuesFor dictionary

using System;
using System.Collections.Generic;
using System.Reflection;

public class Program
{
    private static Dictionary<string, object[]> ValuesFor = new Dictionary<string, object[]>()
    {
        { typeof(int).ToString(), new object[] { 0, 1, 2, 3, 4, 5 } },
        { typeof(bool).ToString(), new object[] { true, false } }
    };



    public static void Main()
    {
        var properties = typeof(A).GetProperties(BindingFlags.Instance | BindingFlags.Public);
        int[] valueIndices = new int[properties.Length];

        do
        {
            createObject(properties, valueIndices);
        } while (setNext(properties, valueIndices));

        Console.ReadKey();
    }

    /// <summary>
    /// This updates the valueIndex array to the next value
    /// </summary>
    /// <returns>returns true if the valueIndex array has been updated</returns>
    public static bool setNext(PropertyInfo[] properties, int[] valueIndices)
    {
        for (var i = 0; i < properties.Length; i++)
        {
            if (valueIndices[i] < ValuesFor[properties[i].PropertyType.ToString()].Length - 1) {
                valueIndices[i]++;
                for (var j = 0; j < i; j++)
                    valueIndices[j] = 0;
                return true;
            }
        }
        return false;
    }

    /// <summary>
    /// Creates the object
    /// </summary>
    public static void createObject(PropertyInfo[] properties, int[] valueIndices)
    {
        var a = new A();
        for (var i = 0; i < properties.Length; i++)
        {
            properties[i].SetValue(a, ValuesFor[properties[i].PropertyType.ToString()][valueIndices[i]]);
        }

        print(a, properties);
    }



    public static void print(object a, PropertyInfo[] properties)
    {
        Console.Write("Created : ");
        for (var i = 0; i < properties.Length; i++)
            Console.Write(properties[i].Name + "=" + properties[i].GetValue(a) + " ");
        Console.Write("\n");
    }



    public class A
    {
        public int value { get; set; }
        public bool foo { get; set; }
        public bool bar { get; set; }
        public bool boo { get; set; }
    }
}

Currently createObject, just creates the object and calls print to print out the object. Instead, you could add it to a collection and run your tests with it (or directly put your tests instead of print)


Fiddle - https://dotnetfiddle.net/oeLqc5

potatopeelings
  • 40,709
  • 7
  • 95
  • 119