我有一个字符串列表,每个字符串都是可变长度的。我想从列表中投出一个子集,该子集具有从长度等于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中看到快捷方式)
答案 0 :(得分: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);
答案 1 :(得分: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;
}
答案 2 :(得分: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
答案 3 :(得分:0)
我相信这可以做你想要的:
var filteredList = items.Where(x => x.Length < 5)
.SelectMany(x => items, (y, z) => { return Execute(y, z); })
.Where(x => x != null).Distinct().ToList();
答案 4 :(得分: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);
}
答案 5 :(得分: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