2012-07-04 30 views
5

Dưới đây là một kịch bản của câu hỏi của tôi: Tôi có một mảng, nói:LINQ để đếm Tiếp tục lặp lại các mục (int) trong một mảng int?

{ 4, 1, 1, 3, 3, 2, 5, 3, 2, 2 } 

Kết quả sẽ được một cái gì đó như thế này (phần tử mảng => tính của nó):

4 => 1 
1 => 2 
3 => 2 
2 => 1 
5 => 1 
3 => 1 
2 => 2 

Tôi biết điều này có thể đạt được bằng for loop.

Nhưng google rất nhiều để làm điều này có thể bằng cách sử dụng các dòng mã nhỏ hơn bằng LINQ mà không thành công.

+3

Thật khó để xem như thế nào chiều dài mảng có thể được cố định ở mức 6 nếu một ví dụ là 4, 1, 1, 3, 3, 2, 5, 3, 2, 2 ... –

+0

Bây giờ hãy kiểm tra câu hỏi, vui lòng – sandeep

+0

Tại sao đầu ra có các khóa trùng lặp cho các số "2" và "3"? Bạn không nên mong đợi một đầu ra là "4 => 1, 1 => 2, 3 => 3, 2 => 3, 5 => 1"? –

Trả lời

8

Tôi tin rằng tối ưu nhất cách để làm điều này là tạo ra một phương thức "LINQ giống như" mở rộng bằng cách sử dụng một khối lặp. Điều này cho phép bạn thực hiện phép tính thực hiện một lần truyền dữ liệu của bạn. Lưu ý rằng hiệu suất không quan trọng chút nào nếu bạn chỉ muốn thực hiện phép tính trên một mảng nhỏ các số. Tất nhiên đây thực sự là vòng lặp của bạn trong ngụy trang.

static class Extensions { 

    public static IEnumerable<Tuple<T, Int32>> ToRunLengths<T>(this IEnumerable<T> source) { 
    using (var enumerator = source.GetEnumerator()) { 
     // Empty input leads to empty output. 
     if (!enumerator.MoveNext()) 
     yield break; 

     // Retrieve first item of the sequence. 
     var currentValue = enumerator.Current; 
     var runLength = 1; 

     // Iterate the remaining items in the sequence. 
     while (enumerator.MoveNext()) { 
     var value = enumerator.Current; 
     if (!Equals(value, currentValue)) { 
      // A new run is starting. Return the previous run. 
      yield return Tuple.Create(currentValue, runLength); 
      currentValue = value; 
      runLength = 0; 
     } 
     runLength += 1; 
     } 

     // Return the last run. 
     yield return Tuple.Create(currentValue, runLength); 
    } 
    } 

} 

Lưu ý rằng phương pháp mở rộng là chung chung và bạn có thể sử dụng nó ở bất kỳ loại nào. Giá trị được so sánh cho sự bình đẳng sử dụng Object.Equals. Tuy nhiên, nếu bạn muốn bạn có thể vượt qua một số IEqualityComparer<T> để cho phép tùy chỉnh cách so sánh giá trị.

Bạn có thể sử dụng phương pháp như thế này:

var numbers = new[] { 4, 1, 1, 3, 3, 2, 5, 3, 2, 2 }; 
var runLengths = numbers.ToRunLengths(); 

Đối với bạn dữ liệu đầu vào kết quả sẽ là những tuples:

 
4 1 
1 2 
3 2 
2 1 
5 1 
3 1 
2 2 
+0

vẻ như tôi đã hơi chậm 1 – Jodrell

+0

Có một adavantage trong sử dụng 'GetEnumerator' hoặc là nó chỉ để tránh 'foreach' theo yêu cầu? – Jodrell

+0

@Jodrell: Khi tôi phải xử lý đặc biệt yếu tố đầu tiên, nó phải đi ra ngoài vòng lặp chính. Sử dụng điều tra viên cho phép tôi làm điều đó và vẫn chỉ truy xuất và kiểm tra từng phần tử chính xác một lần. –

-4

Hãy thử GroupBy qua List<int>

 List<int> list = new List<int>() { 4, 1, 1, 3, 3, 2, 5, 3, 2, 2 }; 
     var res = list.GroupBy(val => val); 
     foreach (var v in res) 
     { 
      MessageBox.Show(v.Key.ToString() + "=>" + v.Count().ToString()); 
     } 
+0

downvoting y mà không biết điều gì? –

+0

tôi cũng đã thiết kế cùng một phương pháp trên vì vậy tôi cũng nghĩ rằng không cần downvoting –

+4

Tôi nghĩ rằng mọi người downvote bạn bởi vì đây không phải là câu trả lời đúng. Ý tưởng của anh ta là nhóm ** các lần xuất hiện ** liên tục. –

0
var array = new int[]{};//whatever ur array is 
array.select((s)=>{return array.where((s2)=>{s == s2}).count();}); 

prob chỉ với là THT nếu bạn có 1 - hai lần bạn sẽ nhận được kết quả trong vòng 1 hai lần

3

(Thêm một câu trả lời để tránh hai upvotes cho một xóa của tôi đếm theo hướng này ...)

Tôi đã có một chút suy nghĩ về điều này (bây giờ tôi đã hiểu câu hỏi) và nó thực sự là không rõ ràng như thế nào bạn muốn làm điều này độc đáo trong LINQ. Có những cách chắc chắn rằng nó có thể được thực hiện, có khả năng sử dụng Zip hoặc Aggregate, nhưng chúng sẽ tương đối không rõ ràng. Sử dụng foreach khá đơn giản:

// Simplest way of building an empty list of an anonymous type... 
var results = new[] { new { Value = 0, Count = 0 } }.Take(0).ToList(); 

// TODO: Handle empty arrays 
int currentValue = array[0]; 
int currentCount = 1; 

foreach (var value in array.Skip(1)) 
{ 
    if (currentValue != value) 
    { 
     results.Add(new { Value = currentValue, Count = currentCount }); 
     currentCount = 0; 
     currentValue = value; 
    } 
    currentCount++; 
} 
// Handle tail, which we won't have emitted yet 
results.Add(new { Value = currentValue, Count = currentCount }); 
2

Điều này không đủ ngắn?

public static IEnumerable<KeyValuePair<T, int>> Repeats<T>(
     this IEnumerable<T> source) 
{ 
    int count = 0; 
    T lastItem = source.First(); 

    foreach (var item in source) 
    { 
     if (Equals(item, lastItem)) 
     { 
      count++; 
     } 
     else 
     { 
      yield return new KeyValuePair<T, int>(lastItem, count); 
      lastItem = item; 
      count = 1; 
     } 
    } 

    yield return new KeyValuePair<T, int>(lastItem, count); 
} 

Tôi sẽ quan tâm để xem một cách LINQ.

+0

Còn vật phẩm cuối cùng thì sao? – Gabe

+0

@Gabe điểm tốt – Jodrell

+0

Tôi đã tự do sửa một vài lỗi trong mã cho phép nó biên dịch. –

3

Dưới đây là một biểu thức LINQ làm việc (chỉnh sửa: hơn thắt chặt lên mã chỉ là một chút):

var data = new int[] { 4, 1, 1, 3, 3, 2, 5, 3, 2, 2 }; 
var result = data.Select ((item, index) => 
         new 
         { 
          Key = item, 
          Count = (index == 0 || data.ElementAt(index - 1) != item) 
           ? data.Skip(index).TakeWhile (d => d == item).Count() 
           : -1 
         } 
         ) 
        .Where (d => d.Count != -1); 

here's a proof cho thấy nó làm việc.

+0

1 để giảm thiểu việc sử dụng của ';' như OP yêu cầu – Jodrell

+0

Trong cách này là câu trả lời chính xác bởi vì nó sử dụng LINQ như đã nêu trong câu hỏi. Tò mò về hiệu quả của phương pháp này tôi đã đếm số lượng điều tra được tạo ra và số lượng các mục được truy cập cho mảng đầu vào gồm 10 phần tử. 17 điều tra viên đã được tạo ra và 101 phần tử được truy cập so với cách tiếp cận tối ưu nhất trong đó có 1 điều tra viên được tạo ra và 10 phần tử được truy cập. –

1

Tôi đã viết phương thức bạn cần trên there. Đây là cách gọi nó.

foreach(var g in numbers.GroupContiguous(i => i)) 
{ 
    Console.WriteLine("{0} => {1}", g.Key, g.Count); 
} 
1

Nầy (bạn có thể chạy trực tiếp trong này LINQPad - rle là nơi sự kỳ diệu sẽ xảy ra):

var xs = new[] { 4, 1, 1, 3, 3, 2, 5, 3, 2, 2 }; 

var rle = Enumerable.Range(0, xs.Length) 
        .Where(i => i == 0 || xs[i - 1] != xs[i]) 
        .Select(i => new { Key = xs[i], Count = xs.Skip(i).TakeWhile(x => x == xs[i]).Count() }); 

Console.WriteLine(rle); 

Tất nhiên, đây là O (n^2), nhưng bạn đã không yêu cầu hiệu suất tuyến tính trong spec.

0
var array = new int[] {1,1,2,3,5,6,6 }; 
var arrayd = array.Distinct(); 
var arrayl= arrayd.Select(s => { return array.Where(s2 => s2 == s).Count(); }).ToArray(); 

Output

arrayl=[0]2 [1]1 [2]1 [3]1 [4]2 
+0

Chào mừng bạn đến với Stack Overflow! Hãy dành một phút để đọc qua [Cách trả lời] (http://stackoverflow.com/questions/how-to-answer) - điều này có vẻ hữu ích nhưng nó sẽ được hưởng lợi từ một số giải thích về mã, xem xét [sửa] http://stackoverflow.com/posts/41385887/edit)-ing that in? –

0
var array = new int[] {1,1,2,3,5,6,6 }; 
foreach (var g in array.GroupBy(i => i)) 
{ 
    Console.WriteLine("{0} => {1}", g.Key, g.Count()); 
} 
+1

Tốt hơn nên bao gồm một số bối cảnh/giải thích với mã vì điều này làm cho câu trả lời hữu ích hơn cho OP và cho người đọc trong tương lai. – EJoshuaS

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