2013-10-15 43 views
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中的快捷方式,以及如果可能的話)

+0

我米不知道我理解的要求。爲什麼不是ababc(ab + abc)所需的輸出? – recursive

+0

因爲abc + ab已經產生了輸出,所以你不需要再考慮abc和ab,直到找到下一個abc或ab。 – Mike

+0

我想你正在尋找[cross join](http://stackoverflow.com/questions/56547/how-do-you-perform-a-cross-join-with-linq-to-sql)filteredList到自己,而不是「Aggregate」調用(假設「所有組合」意味着所有組合:)) –

回答

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 
+1

這提供了與自己連接的物品。目前尚不清楚是否應納入或排除。這也不會顯示示例中的單個字符串值;它只顯示對。 – Servy

+0

不,你的錯過了最後一個,即ab + cde。正如我所說,如果該項目再次發生,應該考慮。 – Mike

+0

@Anirudh:這也是錯誤的。 abcab正在重複 – Mike

0

我相信這就是你想要的:

var filteredList = items.Where(x => x.Length < 5) 
    .SelectMany(x => items, (y, z) => { return Execute(y, z); }) 
    .Where(x => x != null).Distinct().ToList(); 
+0

這包括(ab + abc)以及(abc + ab)。其中只有一個應該出現在輸出中。 – recursive

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); 
+0

爲什麼'.Take(items.Count - i - 1)'?看起來你特別排除了最後一個元素。 – recursive

+0

糟糕...實際上我想排除當前元素,以便不將它與自身連接起來。所以我用Skip(i + 1)替換了Skip(i)...並且我不排除最後一個元素!謝謝你的評論! – schglurps

+0

好的,我明白了。在這種情況下,'Take'是多餘的,因爲默認情況下,IEnumerable將枚舉所有剩餘的元素。 – recursive

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; 
    } 
+0

它不會帶來任何輸出 – Mike

+0

修復它,謝謝。我忘了放!在string.IsNullOrEmpty之前。 –

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); 
} 
+0

.NET 3.5中Zip的替換是什麼 – Mike

+0

@Mike只需重新實現它;寫起來很簡單。只需接受這兩個序列,同時迭代它們並在當前項目上產生選擇器的結果。 – Servy

+0

它只會產生錯誤的aabcd和abcde。 – Mike

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