2013-08-01 18 views
5

Tôi có một IEnumerable và tôi muốn chia dữ liệu trên 3 cột bằng cách sử dụng logic nghiệp vụ sau. nếu 3 hoặc ít hơn các mục, 1 mục cho mỗi cột, bất cứ điều gì khác tôi muốn chia tổng số mục bằng cách chia 3 phần còn lại (hoặc 1 hoặc 2 mục) giữa hai cột đầu tiên. Bây giờ điều này là khá xấu xí nhưng nó thực hiện công việc. Tôi đang tìm lời khuyên để tận dụng LINQ tốt hơn một chút hoặc có thể loại bỏ các tuyên bố chuyển đổi. Bất kỳ lời khuyên hoặc lời khuyên nào để cải thiện mã đều được đánh giá cao.Trình biên dịch thuật toán C# tách một mảng thành 3 phần?

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

Rốt cuộc tôi đang sử dụng chúng trong một cái nhìn Razor MVC

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

Trong nỗ lực đầu tiên của tôi, tôi muốn thoát khỏi colNItems và các biến colNTake nhưng tôi không thể tìm ra các thuật toán chính xác để làm cho nó hoạt động giống nhau.

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

Câu hỏi này có lẽ phù hợp hơn cho http://codereview.stackexchange.com/ –

+0

Suy nghĩ đệ quy! quy tắc còn lại về nguyên tắc giống như quy tắc 'nhỏ hơn 3', lần lượt tương tự như quy tắc phân chia – Polity

Trả lời

1

Bạn có thể khái quát:

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

đã không được thử nghiệm, nhưng điều này sẽ làm việc cho bất kỳ số cột.

Ngoài ra bất cứ khi nào bạn cần biến col1, col2, col3 nghĩ về col [0], col [1], col [2].

+0

Điều này không tạo ra 3 danh sách? – Hogan

+0

Mã bây giờ tạo danh sách. Ngoài ra tôi tin rằng nó phù hợp với ý tưởng của OP về việc giữ các yếu tố liên tiếp trong cùng một cột – rslite

+0

Tôi đã kết thúc bằng cách sử dụng phương pháp này. Dường như đủ sạch sẽ và đơn giản. Tất cả các giải pháp tuyệt vời mặc dù tất cả mọi người. Tôi chắc chắn thích cách tiếp cận css thuần túy từ Brian Ball. Cảm ơn mọi người! – Hcabnettek

1

Bạn không thể làm điều gì đó như thế?

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; 

Edit:

Một giải pháp chung chung hơn mà làm việc cho bất kỳ số lượng các cột (COLUMNS) sẽ là:

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

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

Điều này không có ý nghĩa gì, bạn chỉ cần đếm các mục? Bạn không muốn đặt kết quả vào một danh sách? – Hogan

+0

Nếu bạn đọc toàn bộ bài đăng gốc, bạn sẽ thấy rằng câu hỏi là "có cách nào để loại bỏ câu lệnh chuyển đổi không?". Tôi đã làm điều đó với đoạn mã này. –

+0

Tôi hiểu ý bạn là gì, tuy nhiên tôi nghĩ anh ấy cần một giải pháp giúp cho danh sách nhanh hơn. – Hogan

0

Nếu bạn muốn điền vào các cột round-robin bạn có thể sử dụng:

int numColumns = 3; 

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

dễ thương, nhưng bạn lặp lại danh sách 3 lần thay vì một lần. – Hogan

6

Là các cột f ixed-width? Nếu vậy, thì không cần phải làm gì đặc biệt với bộ sưu tập của bạn. Chỉ cần dựa vào trình duyệt để làm điều đó cho bạn. Có một thùng chứa bên ngoài có chiều rộng tổng thể của 3 cột, sau đó chỉ cần điền nó với một div cho mỗi mục (và nổi bên trái). Đặt các thùng chứa bên trong của bạn để có chiều rộng chính xác 1/3 của thùng chứa bên ngoài.

Dưới đây là một cách nhanh chóng fiddle

Dưới đây là một gợi ý nhanh các phong cách

div#outer{ 
    width:300px;  
} 

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

+1, giải pháp tốt, bên ngoài hộp. (pun dự định) – Hogan

+0

Về giải pháp này, họ muốn các mục "theo chiều dọc" thái lát nếu bạn muốn, như trong các mục 1-bất cứ điều gì sẽ hiển thị trong cùng một cột, không ngang như trong fiddle. Giải pháp tuyệt vời mặc dù !! – Hcabnettek

0

nó có thể giúp

 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

Đây không phải là nhanh, nhưng nó sẽ làm các trick:

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

Nó sử dụng phiên bản Chọn bao gồm tham số chỉ mục. Bạn có thể sử dụng GroupBy để tăng tốc độ này một chút với chi phí của một vài dòng mã.

0

Nếu bạn muốn xem hết câu trả lời dưới đây, nếu bạn muốn đi xuống thì bạn có thể làm như sau (sử dụng mã này với kiểm tra bên dưới để xem nó hoạt động thay vì dòng 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 
       }; 
      }); 

Bạn có thể làm điều này với tổng Nó sẽ trông như thế này:

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

Bạn kết thúc với một đối tượng với một mảng của 3 liệt kê (một cho mỗi cột).

Ví dụ này sẽ chạy như trong linqPad. Tôi recomment linqPad cho các loại thử nghiệm POC. (LinqPad.com)

0

LinqLib (NuGet: LinqExtLibrary) có một tình trạng quá tải của ToArray() nào đó:

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

Tôi đã thử điều này và nó không hoạt động chút nào. – Hogan

1

Vì vậy, bạn muốn n đầu tiên/3 mục trong cột đầu tiên, bên cạnh n 3 mục/trong 2 cột, v.v.

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

Tôi tạo danh sách cụ thể để tránh lặp lại Enumerable nhiều lần. Ví dụ: nếu bạn có:

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

Sau đó, bạn sẽ không thể lặp lại nhiều lần.

Các vấn đề liên quan