2013-08-01 25 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"); 

然后您将无法多次迭代它。