【问题标题】:Generate all Combinations from Multiple (n) Lists从多个 (n) 列表中生成所有组合
【发布时间】:2015-12-10 19:46:39
【问题描述】:

编辑:我正在完全重做我的问题,因为我已经找到了最简单的提问方式。感谢到目前为止让我思考根本问题的评论者。

public List<string> GetAllPossibleCombos(List<List<string>> strings)
{
    List<string> PossibleCombos = new List<string>();

    //????
    {
        string combo = string.Empty;
        // ????
        {
            combo += ????
        }
        PossibleCombos.Add(combo);
    }

    return PossibleCombos;
}

我需要弄清楚如何递归地遍历每个 List&lt;string&gt; 并将每个列表中的 1 个字符串组合成一个组合 string。不要太担心格式化字符串,因为“实时”代码使用自定义对象。此外,请随意假设每个列表将包含至少 1 个字符串并且没有空值。

【问题讨论】:

  • 您的代码如何映射给定问题有点不清楚:输入似乎是一个整数?输出只有一个字符串列表?您能否提供一个更容易理解的问题帐户。此外:是否需要一次生成所有可能的组合?也许惰性实现更有趣。
  • 您可能想阅读关于Producing combinations 的 Eric Lipperts 系列
  • 输入只是模板的 ID - 该模板只包含模板引用的所有标签的列表。代码的第一部分并不重要。它只是用来大致了解字典中的内容。重要的是结合字典中的所有项目。
  • 好的。 2)你如何“组合”字符串?你附加它们吗?你在两者之间使用标记吗?是否必须返回列表列表?
  • @AnthonyNichols:解决方案是对列表数量使用递归:解决一个列表的基本情况的问题,然后解决n 列表的问题(通过将其解决为与第一个列表和其他 n-1 列表的组合合并)。

标签: c# linq list dictionary


【解决方案1】:

这是一个简单的非递归解决方案,它只是连接每个组合的元素:

public static List<string> GetAllPossibleCombos(List<List<string>> strings)
{
    IEnumerable<string> combos = new [] { "" };

    foreach (var inner in strings)
        combos = from c in combos
                 from i in inner
                 select c + i;

    return combos.ToList();
}

static void Main(string[] args)
{
    var x = GetAllPossibleCombos(
        new List<List<string>>{
            new List<string> { "a", "b", "c" },
            new List<string> { "x", "y" },
            new List<string> { "1", "2", "3", "4" }});
}

您可以将其概括为返回一个IEnumerable&lt;IEnumerable&lt;string&gt;&gt;,它允许调用者应用他们喜欢的任何操作来将每个组合转换为字符串(例如下面的string.Join)。使用延迟执行枚举组合。

public static IEnumerable<IEnumerable<string>> GetAllPossibleCombos(
    IEnumerable<IEnumerable<string>> strings)
{
    IEnumerable<IEnumerable<string>> combos = new string[][] { new string[0] };

    foreach (var inner in strings)
        combos = from c in combos
                 from i in inner
                 select c.Append(i);

    return combos;
}

public static IEnumerable<TSource> Append<TSource>(
    this IEnumerable<TSource> source, TSource item)
{
    foreach (TSource element in source)
        yield return element;

    yield return item;
}

static void Main(string[] args)
{
    var combos = GetAllPossibleCombos(
        new List<List<string>>{
            new List<string> { "a", "b", "c" },
            new List<string> { "x", "y" },
            new List<string> { "1", "2", "3", "4" }});

    var result = combos.Select(c => string.Join(",", c)).ToList();
}

【讨论】:

  • 我试过了,但无法让它与我的自定义对象一起使用。我认为(简化我的问题部分是我的错)我的对象太复杂而无法使用此设置。不过这个想法很棒,我很感激你分享它。当我将来需要这样的东西时,我会记住它!
  • 这应该是答案。 A+
【解决方案2】:

希望这会有所帮助。

class NListBuilder

{
    Dictionary<int, List<string>> tags = new Dictionary<int, List<string>>();

    public NListBuilder()
    {
        tags.Add(1, new List<string>() { "A", "B", "C" });
        tags.Add(2, new List<string>() { "+", "-", "*" });
        tags.Add(3, new List<string>() { "1", "2", "3" });
    }

    public List<string> AllCombos
    {
        get
        {
            return GetCombos(tags);
        }
    }

    List<string> GetCombos(IEnumerable<KeyValuePair<int, List<string>>> remainingTags)
    {
        if (remainingTags.Count() == 1)
        {
            return remainingTags.First().Value;
        }
        else
        {
            var current = remainingTags.First();
            List<string> outputs = new List<string>();
            List<string> combos = GetCombos(remainingTags.Where(tag => tag.Key != current.Key));

            foreach (var tagPart in current.Value)
            {
                foreach (var combo in combos)
                {
                    outputs.Add(tagPart + combo);
                }
            }

            return outputs;
        }


    }
}

【讨论】:

  • 谢谢,不要更改您的答案以匹配“新”问题。我正在尝试将其转换为我的“真实”系统。它认为它会起作用。如果我有任何问题,我会告诉你。
  • 所以花了一些时间,但我的最终解决方案与您的非常相似。我不确定这是您的代码还是我的转换,但第一部分 (...Count() == 1) 让我搞砸了一切。我把它拿出来后效果很好。感谢您让我朝着正确的方向前进。
  • @Anthony:使用整数类型可能会导致无法应用于对象的快捷方式...无论如何,不​​客气 :)
  • 请注意:对属性主体执行耗时的方法是一种不好的做法。
【解决方案3】:

如果它对任何人有帮助,这里是 Douglas 的 GetAllPossibleCombos 方法的方法语法版本。

 public static List<string> GetAllPossibleCombos(List<List<string>> strings)
 {
     IEnumerable<string> combos = new[] { "" };

     foreach (var inner in strings)
     {
         combos = combos.SelectMany(r => inner.Select(x => r + x));
     }

     return combos.ToList();
 }

【讨论】:

  • 不错的解决方案!我为通用对象类型扩展并创建了一个(见回复)
【解决方案4】:

这是一个适用于所有对象类型的通用版本:

    public static List<List<T>> GetAllPossibleCombos<T>(List<List<T>> objects)
    {
        IEnumerable<List<T>> combos = new List<List<T>>() { new List<T>() };

        foreach (var inner in objects)
        {
            combos = combos.SelectMany(r => inner
            .Select(x => {
                var n = r.DeepClone();
                if (x != null)
                {
                    n.Add(x);
                }
                return n;
            }).ToList());
        }

        // Remove combinations were all items are empty
        return combos.Where(c => c.Count > 0).ToList();
    }

如果您提供空值,它也会为您提供空组合。例如:

        var list1 = new List<string>() { "1A", "1B", null };
        var list2 = new List<string>() { "2A", "2B", null };
        var output = GetAllPossibleCombos(allLists);

将包含:

[["1A"], ["1B"], ["2A"], ["2B"], ["1A", "2A"], ["1A", "2B"], [" 1B", "2A"], ["1B," "2B"]]

而不仅仅是:

        var list1 = new List<string>() { "1A", "1B" };
        var list2 = new List<string>() { "2A", "2B" };
        var output = GetAllPossibleCombos(allLists);

[[“1A”、“2A”]、[“1A”、“2B”]、[“1B”、“2A”]、[“1B”、“2B”]]

注意:DeepClone 是一种用于复制列表的扩展方法。这可以通过多种方式完成

    public static T DeepClone<T>(this T source)
    {
        // Don't serialize a null object, simply return the default for that object
        if (Object.ReferenceEquals(source, null))
        {
            return default(T);
        }

        var deserializeSettings = new JsonSerializerSettings { ObjectCreationHandling = ObjectCreationHandling.Replace };

        return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(source), deserializeSettings);

    }

【讨论】:

    【解决方案5】:

    这是一个适用于任何泛型类型的答案,它还附带一个将 base-10 转换为 base-n 的函数。

    public static class IntExt
    {
        const string Symbols = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
        public static string ToString(this int value, int toBase)
        {
            switch (toBase)
            {
                case 2:
                case 8:
                case 10:
                case 16:
                    return Convert.ToString(value, toBase);
    
                case 64:
                    return Convert.ToBase64String(BitConverter.GetBytes(value));
    
                default:
                    if (toBase < 2 || toBase > Symbols.Length)
                        throw new ArgumentOutOfRangeException(nameof(toBase));
    
                    if (value < 0)
                        throw new ArgumentOutOfRangeException(nameof(value));
    
                    int resultLength = 1 + (int)Math.Max(Math.Log(value, toBase), 0);
                    char[] results = new char[resultLength];
                    int num = value;
                    int index = resultLength - 1;
                    do
                    {
                        results[index--] = Symbols[num % toBase];
                        num /= toBase;
                    }
                    while (num != 0);
    
                    return new string(results);
            }
        }
    }
    
    public class UnitTest1
    {
        public static T[][] GetJoinCombinations<T>(T[][] arrs)
        {
            int maxLength = 0;
            int total = 1;
            for (int i = 0; i < arrs.Length; i++)
            {
                T[] arr = arrs[i];
                maxLength = Math.Max(maxLength, arr.Length);
                total *= arr.Length;
            }
    
            T[][] results = new T[total][];
            int n = 0;
            int count = (int)Math.Pow(maxLength, arrs.Length);
            for (int i = 0; i < count; i++)
            {
                T[] combo = new T[arrs.Length];
                string indices = i.ToString(maxLength).PadLeft(arrs.Length, '0');
                bool skip = false;
                for (int j = 0; j < indices.Length; j++)
                {
                    T[] arr = arrs[j];
                    int index = int.Parse(indices[j].ToString());
                    if (index >= arr.Length)
                    {
                        skip = true;
                        break;
                    }
    
                    combo[j] = arr[index];
                }
    
                if (!skip)
                    results[n++] = combo;
            }
    
            return results;
        }
    
        [Fact]
        public void Test1()
        {
            string[][] results = GetJoinCombinations(new string[][]
            {
                new string[] { "1", "2", "3" },
                new string[] { "A", "B", "C" },
                new string[] { "+", "-", "*", "/" },
            });
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2013-06-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多