1

我有一个字符串列表,每个字符串都是可变长度的。我想从列表中投影一个子集,该子集的字符串从原始列表连接起来,长度等于 5。我正在使用聚合函数,但它没有给我想要的结果。对于这个投影,什么是合适的 LINQ 查询?你能帮忙吗?

代码:

class Program
{
    static void Main(string[] args)
    {
        IEnumerable<string> items = new List<string> {"abc", "ab", "abcd", "abcde", "abcdef", "a", "ab", "cde"};

        //All combinations of concatenations equal to length 5.
        //Only need to process an item once and need to concatenate 2 items and no more

        var filteredList = items.Where(x => x.Length < 5)
                                .Aggregate(Execute).ToList();

        foreach (var f in filteredList)
        {
            //Should out put : abc+ab = abcab
            //Should out put : abcde
            //Should out put : abcd+a = abcda
            //Should out put : ab+cde = abcde
            Console.WriteLine(f);
        }
    }

    private static string Execute(string a, string b)
    {
        if (string.IsNullOrEmpty(a) || string.IsNullOrEmpty(b))
            return null;

        if ((a.Length + b.Length) == 5)
            return a + b;

        return null;
    }
}

几点:

  • 处理完一个项目后,我不需要再次考虑该项目进行组合

  • 以上是正确的,直到我在列表中再次找到相同的项目,一旦我找到它,我应该尝试将它与另一个未在先前连接中使用的项目连接。

  • 不需要它是 LINQ,我只是在寻找解决方案。

  • 输出不能包含两个以上的字符串?(a + bc + de) 不是必需的。

  • 项目不需要与自身连接。

  • 我已经提到输出作为问题的一部分。

注意:使用 .NET 3.5(但如果可能的话,也希望看到 .NET 4.0 中的快捷方式)

4

6 回答 6

1

如果你问我,我会说:“不要偷懒”。

    private static List<string> ValidCombinationsFind(List<string> iSource)
    {
        List<string> lstResult = new List<string>();

        //Use and explicit mask to remember indexes in iSource which were used.
        bool[] ablnUsageMask = new bool[iSource.Count];

        int intCurrentIndex = 0;

        //Examine the source list one by one.
        while (intCurrentIndex < iSource.Count - 1)
        {
            //If the next item is not already used then go on.
            if (!ablnUsageMask[intCurrentIndex])
            {
                string strCurrentItem = iSource[intCurrentIndex];

                //If the item is ok then check every remaining item for a match.
                if (!string.IsNullOrEmpty(strCurrentItem))
                {
                    //Check if an item fits on its own.
                    if (strCurrentItem.Length == 5)
                    {
                        ablnUsageMask[intCurrentIndex] = true;
                        lstResult.Add(strCurrentItem);
                    }
                    else
                    {
                        for (int intNextItemIndex = intCurrentIndex + 1; intNextItemIndex < iSource.Count; intNextItemIndex++)
                        {
                            //If the next item is not already used then go on.
                            if (!ablnUsageMask[intNextItemIndex])
                            {
                                string strNextItem = iSource[intNextItemIndex];

                                if (!string.IsNullOrEmpty(strNextItem))
                                {
                                    if ((strCurrentItem.Length + strNextItem.Length) == 5)
                                    {
                                        ablnUsageMask[intCurrentIndex] = true;
                                        ablnUsageMask[intNextItemIndex] = true;
                                        lstResult.Add(strCurrentItem + strNextItem);
                                        break;
                                    }
                                }
                            }
                        }
                    }
                }
            }

            intCurrentIndex++;
        }

        return lstResult;
    }
于 2013-10-15T17:14:05.817 回答
1
        List<string> items = new List<string> { "abc", "ab", "abcd", "abcde", "abcdef", "a", "ab", "cde" };
        var result = items
                        .SelectMany((x, i) => items.Skip(i + 1).Concat(new[] {string.Empty}), (s1, s2) => s1 + s2)
                        .Where(s => s.Length == 5);
于 2013-10-15T17:04:37.650 回答
0

使用双for循环同时跟踪哪些索引给出了匹配应该这样做:

IEnumerable<string> items = new List<string> { "abc", "ab", "abcd", "abcde", "abcdef", "a", "ab", "cde" };
var filteredList = GimmeFives(items).ToList();

private IEnumerable<string> GimmeFives(IEnumerable<string> items)
{
    //"Once an item is processed, I dont need to consider that item again for a combination"
    var indexesProcessed = new List<int>();

    for (int i = 0; i < items.Count(); i++)
    {
        if (indexesProcessed.Contains(i)) { continue; }

        var first = items.ElementAt(i);
        if (first.Length == 5)
        {
            yield return first;
        }
        else
        {
            //Start the second loop after index "i", to avoid including previously processed items:
            for (int j = i+1; j < items.Count(); j++)
            {
                if (indexesProcessed.Contains(j)) { continue; }

                var second = items.ElementAt(j);

                if ((first.Length + second.Length) == 5)
                {
                    //Remove the middle "+" sign in production code...
                    yield return (first + "+" + second);

                    indexesProcessed.Add(i);
                    indexesProcessed.Add(j);

                    //"Once an item is processed, I dont need to consider that item again for a combination"
                    //"first" has gotten its match, so we don't need to search for another "second":
                    break;
                }
            }
        }

    }
}

输出:

abc+ab
abcd+a
abcde
ab+cde
于 2013-10-15T18:54:33.693 回答
0
void Main()
{
    var items = new List<string> {"abc", "ab", "abcd", "abcde", "abcdef", "a", "ab", "cde"};
    bool[] flag=new bool[items.Count()];
    var output=items.SelectMany((x,i)=>items.Select((y,j)=>concat(flag,items,i,j)))
                    .Where(x=>x!="");
}

public string concat(bool[] flag,List<string> items,int i,int j)
{
if(flag[i]==false && flag[j]==false && (items[i].Length==5||(items[i]+items[j]).Length==5))
{
      flag[i]=true;
      flag[j]=true;

      if(i==j)return items[i];
      else return items[i]+","+items[j];
}
else return "";
}

输出:

abc,ab 
abcd,a 
abcde 
ab,cde 
于 2013-10-15T16:59:47.080 回答
0

我相信这可以满足您的要求:

  var filteredList = items.Where(x => x.Length < 5)
    .SelectMany(x => items, (y, z) => { return Execute(y, z); })
    .Where(x => x != null).Distinct().ToList();
于 2013-10-15T17:00:02.177 回答
0

所以这里的想法是配对对象,使这两个对象具有完全正确的大小。我们可以在这里做的是按大小对所有字符串进行分组。我们知道,1号和4号的所有物品都需要配对,2号和3号的需要配对,5号的单独就可以了。

因此,我们可以遍历可能的大小,获取具有该大小的组和具有该大小的组来创建所需的项目,然后压缩这两个集合。对于每个索引,Zip 将从第一个组中取出第一个项目,将其与第二个组中的第一个项目匹配,然后依此类推。这意味着每个字符串永远不会重复。然后,我们可以将尺寸完全正确的项目添加到末尾。

private static IEnumerable<string> MakeDesiredSize(
    List<string> items, int desiredSide)
{
    var lookup = items.Where(item => item.Length <= desiredSide)
        .ToLookup(item => item.Length);
    return Enumerable.Range(1, desiredSide / 2)
            .SelectMany(i => lookup[i].Zip(lookup[desiredSide - i]
                , (a, b) => a + b))
            .Concat(lookup[desiredSide]);
}

由于您使用的是 3.5,因此这是一个实现Zip

public static IEnumerable<TResult> Zip<TSource, TResult>(
    this IEnumerable<TSource> first,
    IEnumerable<TSource> second,
    Func<TSource, TSource, TResult> resultSelector)
{
    using (var itOne = first.GetEnumerator())
    using (var itSecond = second.GetEnumerator())
        while (itOne.MoveNext() & itSecond.MoveNext())
            yield return resultSelector(itOne.Current, itSecond.Current);
}
于 2013-10-15T17:23:17.520 回答