2013-08-01 27 views
5

我有一個IEnumerable,我想要使用以下業務邏輯將數據分成3列。如果3個或更少的項目,每列1個項目,我希望將總項目除以3的任何其他項目在前兩列之間分割剩餘物(1或2個項目)。現在這是非常醜陋的,但它完成了這項工作。我正在尋找提示,以便更好地利用linq或可能消除switch語句。任何改善代碼的建議或提示都會被讚賞。C#算法重構將數組分成3部分?

var numItems = items.Count; 

      IEnumerable<JToken> col1Items, 
           col2Items, 
           col3Items; 


      if(numItems <=3) 
      { 
       col1Items = items.Take(1); 
       col2Items = items.Skip(1).Take(1); 
       col3Items = items.Skip(2).Take(1); 

      } else { 

       int remainder = numItems % 3, 
        take = numItems/3, 
        col1Take, 
        col2Take, 
        col3Take; 

       switch(remainder) 
       { 
        case 1: 
         col1Take = take + 1; 
         col2Take = take; 
         col3Take = take; 
         break; 
        case 2: 
         col1Take = take + 1; 
         col2Take = take + 1; 
         col3Take = take; 
         break; 
        default: 
         col1Take = take; 
         col2Take = take; 
         col3Take = take; 
         break; 

       } 

       col1Items = items.Take(col1Take); 
       col2Items = items.Skip(col1Take).Take(col2Take); 
       col3Items = items.Skip(col1Take + col2Take).Take(col3Take); 

最後我在一個MVC Razor視圖使用這些

<div class="widgetColumn"> 
       @Html.DisplayFor(m => col1Items, "MenuColumn")      
      </div> 

      <div class="widgetColumn"> 
       @Html.DisplayFor(m => col2Items, "MenuColumn")      
      </div> 

      <div class="widgetColumn"> 
       @Html.DisplayFor(m => col3Items, "MenuColumn")      
      </div> 

在我第一次嘗試我想擺脫colNItems和colNTake變量,但我不能找出正確的算法使其工作相同。

for (int i = 1; i <= 3; i++) 
      { 
       IEnumerable<JToken> widgets = new List<JToken>(); 
       var col = i; 
       switch(col) 
       { 
        case 1: 
         break; 
        case 2: 
         break; 
        case 3: 
         break; 
       } 
      } 
+5

這個問題可能更適合http://codereview.stackexchange.com/ –

+0

認爲遞歸!剩餘規則原則上與「小於3」的規則相同,後者又與分規則相似 – Polity

回答

1

你可以概括:

int cols = 3; 
IEnumerable<JToken> colItems[3]; // you can make this dynamic of course 

int rem = numItems % cols; 
int len = numItems/cols; 

for (int col=0; col<cols; col++){ 
    int colTake = len; 
    if (col < rem) colTake++; 
    colItems[col] = items.Skip(col*len).Take(colTake); 
} 

沒有測試,但這應該對任何數量的列工作。

此外,無論何時需要變量col1,col2,col3,請考慮col [0],col [1],col [2]。

+0

這不會創建3個列表? – Hogan

+0

代碼現在創建列表。此外,我相信這是符合OP的想法,保持連續的元素在同一列 – rslite

+0

我最終使用這種方法。看起來乾淨和簡單。儘管每個人都有很好的解決方我絕對喜歡Brian Ball的純css方法。感謝大家! – Hcabnettek

1

你不能只是做點什麼嗎?

int len = numItems/3; 
int rem = numItems % 3; 

int col1Take = len + (rem > 0 ? 1 : 0); 
int col2Take = len + (rem > 1 ? 1 : 0); 
int col3Take = len; 

編輯:

,對於任意數量的列(COLUMNS)的作品更通用的解決方案是:

int len = numItems/COLUMNS; 
int rem = numItems % COLUMNS; 

foreach (var i in Enumerable.Range(0, COLUMNS)) { 
    colTake[i] = len + (rem > i ? 1 : 0); 
} 
+0

這沒有任何意義,您只是在計數項目嗎?你不想把結果放在列表中嗎? – Hogan

+0

如果您閱讀完整的原始文章,您會發現問題是「是否有辦法擺脫switch語句?」。我已經用這段代碼片斷完成了。 –

+0

我明白你的意思,但我認爲他需要一個解決方案,使列表更快。 – Hogan

0

如果你想使用的欄循環賽你可以使用:

int numColumns = 3; 

var result = Enumerable.Range(1,numColumns).Select(c => 
     items.Where((x,ix) => ix % numColumns == c-1).ToArray() 
    ); 
+0

很可愛,但是你重複列表3次而不是一次。 – Hogan

6

列f ixed寬?如果是這樣,那麼就沒有必要對你的收藏做任何特別的事情。只要依靠瀏覽器爲你做。有一個外部容器,其總寬度爲3列,然後用每個項目的div填充(並向左浮動)。設置你的內部容器的寬度恰好是外部容器的1/3。

這裏有一個快速fiddle

下面是在風格

div#outer{ 
    width:300px;  
} 

div#outer > div{ 
    width:100px; 
    float:left;  
} 
+0

+1,尼斯解決方案,在箱子外面。 (雙關語) – Hogan

+0

關於這個解決方案,他們希望項目「垂直」切片,如第1項 - 任何會出現在同一列中,不像水平線中的水平。很好的解決方案! – Hcabnettek

0

快速提示它可能會幫助

 IEnumerable<object> items = new Object[]{ "1", "2", "3", "4", "5", "6", "7","8", "9", "10", "11", "12","13", "14" }; 

     IEnumerable<object> col1Items = new List<object>(), 
          col2Items = new List<object>(), 
          col3Items = new List<object>(); 

     Object[] list = new Object[]{col1Items, col2Items, col3Items}; 
     int limit = items.Count()/3; 
     int len = items.Count(); 
     int col;    

     for (int i = 0; i < items.Count(); i++) 
     {     
      if (len == 3) col = i; 
      else col = i/limit; 

      if (col >= 3) col = i%limit ; 

      ((IList<object>)(list[col])).Add(items.ElementAt(i)); 

     } 
0

這並不快,但它會做的伎倆:

var col1Items = items.Select((obj, index) => new { Value = obj, Index = index }) 
    .Where(o => o.Index % 3 == 0).Select(o => o.Value); 
var col2Items = items.Select((obj, index) => new { Value = obj, Index = index }) 
    .Where(o => o.Index % 3 == 1).Select(o => o.Value); 
var col3Items = items.Select((obj, index) => new { Value = obj, Index = index }) 
    .Where(o => o.Index % 3 == 2).Select(o => o.Value); 

它使用包含索引參數的Select版本。您可以使用GroupBy以幾行代碼爲代價加快速度。

0

如果你想穿過那麼下來看下面這個答案,如果你想下去然後跨越你可以這樣做(使用下面的測試代碼來看看它的工作,而不是var result線在那裏:。

var curCol = 0; 
var iPer = items.Count()/3; 
var iLeft = items.Count() % 3; 
var result = items.Aggregate(
       // object that will hold items 
       new { 
         cols = new List<ItemElement>[3] { new List<ItemElement>(), 
                 new List<ItemElement>(), 
                 new List<ItemElement>(), }, 
          }, 
       (o, n) => { 
       o.cols[curCol].Add(n); 

       if (o.cols[curCol].Count() > iPer + (iLeft > (curCol+1) ? 1:0)) 
        curCol++; 

       return new { 
        cols = o.cols 
       }; 
      }); 

你可以用骨料做到這一點它應該是這樣的:

void Main() 
{ 
    List<ItemElement> items = new List<ItemElement>() { 
      new ItemElement() { aField = 1 }, 
      new ItemElement() { aField = 2 }, 
      new ItemElement() { aField = 3 }, 
      new ItemElement() { aField = 4 }, 
      new ItemElement() { aField = 5 }, 
      new ItemElement() { aField = 6 }, 
      new ItemElement() { aField = 7 }, 
      new ItemElement() { aField = 8 }, 
      new ItemElement() { aField = 9 } 
    }; 

    var result = 
    items.Aggregate(
     // object that will hold items 
     new { 
     cols = new List<ItemElement>[3] { new List<ItemElement>(), 
              new List<ItemElement>(), 
              new List<ItemElement>(), }, 
     next = 0 }, 
    // aggregate 
    (o, n) => { 
     o.cols[o.next].Add(n); 

     return new { 
     cols = o.cols, 
     next = (o.next + 1) % 3 
     }; 
    }); 
    result.Dump(); 
} 

public class ItemElement 
{ 
    public int aField { get; set; } 
} 

你用的3名名單(每列)數組中的對象告終。

這個例子將在linqPad中運行。我推薦linqPad進行這些POC測試。 (linqPad.com)

0

LinqLib(的NuGet:LinqExtLibrary)有做它的ToArray(過載):

using System.Collections.Generic; 
using System.Linq; 
using LinqLib.Array; 

... 

    public void TakeEm(IEnumerable<int> data) 
    { 
     var dataAry = data as int[] ?? data.ToArray(); 
     var rows = (dataAry.Length/3) + 1; 
     //var columns = Enumerable.Empty<int>().ToArray(3, rows); 
     // vvv These two lines are the ones that re-arrange your array 
     var columns = dataAry.ToArray(3, rows); 
     var menus = columns.Slice(); 
    } 
+0

我試過了,它根本不起作用。 – Hogan

1

所以,你要在第一N /第一列3項,接下來的N/3項第2列等

var concreteList = items.ToList(); 
var count = concreteList.Count; 
var take1 = count/3 + (count % 3 > 0 ? 1 : 0); 
var take2 = count/3 + (count % 3 > 1 ? 1 : 0); 

var col1 = concreteList.Take(take1); 
var col2 = concreteList.Skip(take1).Take(take2); 
var col3 = concreteList.Skip(take1 + take2); 

我做,以避免Enumerable多次迭代的具體名單。例如,如果您有:

items = File.ReadLines("foo.txt"); 

然後您將無法多次迭代它。