0

I'm stuck with a combination problem. What I got is a Data class which basically holds a name and a list of values:

public abstract class DataField : IDataField
{        
    public string Name { get; set; }           
    public List<string> Values { get; set; }    
}

At runtime what I'm trying to do is to get every possible combination of a List<DataField> object. What i tried so far is:

// Get dataFields with values
List<IDataField> propertyDataFields = mDataFields.Where(x => x.Values.Count > 0).ToList();

var props = GetPropertiesList(propertyDataFields, 0, propertyDataFields.Count - 1,  new List<List<FieldProperties>>());

private static List<List<FieldProperties>> GetPropertiesList(List<IDataField> propertyDataFields, int listPosition, int position, List<List<FieldProperties>> fieldPropertiesList)
{
    var fieldProperties = new List<FieldProperties>();

    foreach (var item in propertyDataFields[position].Values)
    {
        if (position == -1)
        {
            GetPropertiesList(propertyDataFields, listPosition + 1, propertyDataFields.Count - 1, fieldPropertiesList);
        }
        fieldProperties.Add(new FieldProperties(propertyDataFields[position].Name, item));

        GetProperties(propertyDataFields, position - 1, fieldProperties, fieldPropertiesList);
    }

    return fieldPropertiesList;
}

private static void GetProperties(List<IDataField> propertyDataFields, int position, List<FieldProperties> fieldProperties, List<List<FieldProperties>> fieldPropertiesList)
{
    if (position == -1)
    {
        fieldPropertiesList.Add(fieldProperties);
    }
    foreach (var item in propertyDataFields[position].Values)
    {
        fieldProperties.Add(new FieldProperties(propertyDataFields[position].Name, item));
        GetProperties(propertyDataFields, position - 1, fieldProperties, fieldPropertiesList);
    }
}

At the end i need a List of Lists of FieldProperties objects. The idea was to start with the last dataField in list and loop through all other using a foreach everytime, but this won't work if the first list got only 1 entry for example. Maybe sorting by Values. Count is an idea?

Edit: FieldProperties is a class from another dll im using. I need to create an instance for every DataField.Value.

The szenario is: I got a List<IDataField> e.g.

  var dataFieldAuthor = new DataField() {
  Name = "Author"
  Values = new List<string> { "Author1", "Author2", "Author3" };
  }

and i want to create a List<List<FieldProperties>> with all possible combinations of DataField values.

user3292642
  • 711
  • 2
  • 8
  • 32
  • Can you add an example of input and it's matching output? – Maarten Sep 20 '16 at 11:50
  • What is `FieldProperties`? What is your *actual* requirement here? I mean, seems like a horrible technical solution to some made-up problem to be honest. – MarioDS Sep 20 '16 at 11:50
  • So you want 7 `DataField` objects which vary by which Authors are in the list of values? So you'd have, `1,2,3`,`1,2`,`1,3`,`2,3`,`1`,`2` and `3` in the Values field? This seems like a very strange thing to be wanting so just want to make sure I understand correctly. Also I would be inclined to MarioDS that this feels a bit like a bad solution to a different problem... – Chris Sep 20 '16 at 12:06
  • @Chris No Chris. There will be other DataFields like say department which will have again say 5 values, and so on... What i need is Author1 combined with department1, Author1 and Department2 and so on – user3292642 Sep 20 '16 at 12:09
  • It sounds like you want a cartesian product. http://stackoverflow.com/questions/4073713/is-there-a-good-linq-way-to-do-a-cartesian-product may therefore be useful to you as might https://blogs.msdn.microsoft.com/ericlippert/2010/06/28/computing-a-cartesian-product-with-linq/ – Chris Sep 20 '16 at 12:14

1 Answers1

1

You can use the generic method from my answer to How to iterate lists with different lengths to find all permutations?:

public static class Algorithms
{
    public static IEnumerable<T[]> GenerateCombinations<T>(this IReadOnlyList<IReadOnlyList<T>> input)
    {
        var result = new T[input.Count];
        var indices = new int[input.Count];
        for (int pos = 0, index = 0; ;)
        {
            for (; pos < result.Length; pos++, index = 0)
            {
                indices[pos] = index;
                result[pos] = input[pos][index];
            }
            yield return result;
            do
            {
                if (pos == 0) yield break;
                index = indices[--pos] + 1;
            }
            while (index >= input[pos].Count);
        }
    }
}

combined with simple LINQ:

var fields = mDataFields.Where(x => x.Values.Count > 0).ToList();
var result = fields
    .Select(df => df.Values).ToList()
    .GenerateCombinations()
    .Select(c => c.Select((v, i) => new FieldProperties(fields[i].Name, v)).ToList())
    .ToList();
Community
  • 1
  • 1
Ivan Stoev
  • 195,425
  • 15
  • 312
  • 343
  • Thanks for your help. I'm trying to understand your solution but i don't understand semicolon without any statement in front. Can you give me buzzword to google that? – user3292642 Sep 20 '16 at 12:24
  • It's simply a `for` with missing first part (the initializer). As you can see here [for (C# Reference)](https://msdn.microsoft.com/en-us/library/ch45axte.aspx), every `for` statement has 3 sections - `for (initializer; condition; iterator)`, and any of them is optional. – Ivan Stoev Sep 20 '16 at 12:39