2010-10-05 28 views
13

Tôi tự hỏi nếu có chức năng .NET tích hợp để thay đổi từng giá trị trong một mảng dựa trên kết quả của một đại biểu được cung cấp. Ví dụ: nếu tôi có một mảng {1,2,3} và một đại biểu trả về bình phương của mỗi giá trị, tôi muốn có thể chạy một phương thức lấy mảng và ủy quyền và trả về {1,4,9}. Có bất cứ điều gì như thế này đã tồn tại chưa?C#: Thay đổi giá trị cho mỗi mục trong một mảng

+2

Theo truyền thống, điều đó sẽ được gọi là Bản đồ; trong Linq nó được gọi là Chọn. –

Trả lời

19

Không phải là tôi biết (thay thế mỗi yếu tố chứ không phải chuyển sang một mảng mới hoặc trình tự), nhưng nó vô cùng dễ dàng để viết:

public static void ConvertInPlace<T>(this IList<T> source, Func<T, T> projection) 
{ 
    for (int i = 0; i < source.Count; i++) 
    { 
     source[i] = projection(source[i]); 
    } 
} 

Sử dụng:

int[] values = { 1, 2, 3 }; 
values.ConvertInPlace(x => x * x); 

Trong số nếu bạn không thực sự cần để thay đổi mảng hiện có, các câu trả lời khác được đăng bằng cách sử dụng Select sẽ hoạt động tốt hơn. Hoặc phương pháp ConvertAll hiện tại từ .NET 2:

int[] values = { 1, 2, 3 }; 
values = Array.ConvertAll(values, x => x * x); 

Đây là tất cả giả định một mảng một chiều. Nếu bạn muốn bao gồm các mảng hình chữ nhật, nó sẽ phức tạp hơn, đặc biệt nếu bạn muốn tránh đấm bốc.

+1

+1 cho ConvertAll mà thực sự là những gì OP hỏi cho "phương pháp lấy mảng và đại biểu, và trả về ..." –

+2

Tôi không hiểu tại sao điều này lại đánh bại câu trả lời của Nathan cho câu trả lời được chấp nhận. Tôi đang thiếu gì? Chọn chính xác những gì anh ta cần, phải không? –

+0

@Richard: Có thể câu trả lời của Nathan đã hoàn thành khi câu trả lời của tôi được chấp nhận, về mặt hỗ trợ đa chiều. Cách tôi đọc câu hỏi của OP để bắt đầu, tôi nghĩ anh ấy muốn sửa đổi mảng tại chỗ, mà chỉ có câu trả lời của tôi. Đối với mảng để chuyển đổi mảng, 'Array.ConvertAll' là hiệu quả hơn và không yêu cầu .NET 3.5. Nếu chỉ có một chuỗi là bắt buộc, 'Chọn' là tốt như đã đề cập trong câu trả lời của tôi. –

28

LINQ cung cấp hỗ trợ cho các dự báo theo phương pháp Select mở rộng:

var numbers = new[] {1, 2, 3}; 
var squares = numbers.Select(i => i*i).ToArray(); 

Bạn cũng có thể sử dụng một chút ít thông thạo Array.ConvertAll phương pháp:

var squares = Array.ConvertAll(numbers, i => i*i); 

mảng Jagged thể được xử lý bằng cách xếp lồng các dự:

var numbers = new[] {new[] {1, 2}, new[] {3, 4}}; 
var squares = numbers.Select(i => i.Select(j => j*j).ToArray()).ToArray(); 

Mảng đa chiều phức tạp hơn một chút. Tôi đã viết phương thức mở rộng sau đây dự án mọi phần tử trong một mảng đa chiều bất kể thứ hạng của nó là gì.

static Array ConvertAll<TSource, TResult>(this Array source, 
              Converter<TSource, TResult> projection) 
{ 
    if (!typeof (TSource).IsAssignableFrom(source.GetType().GetElementType())) 
    { 
     throw new ArgumentException(); 
    } 
    var dims = Enumerable.Range(0, source.Rank) 
     .Select(dim => new {lower = source.GetLowerBound(dim), 
          upper = source.GetUpperBound(dim)}); 
    var result = Array.CreateInstance(typeof (TResult), 
     dims.Select(dim => 1 + dim.upper - dim.lower).ToArray(), 
     dims.Select(dim => dim.lower).ToArray()); 
    var indices = dims 
     .Select(dim => Enumerable.Range(dim.lower, 1 + dim.upper - dim.lower)) 
     .Aggregate(
      (IEnumerable<IEnumerable<int>>) null, 
      (total, current) => total != null 
       ? total.SelectMany(
        item => current, 
        (existing, item) => existing.Concat(new[] {item})) 
       : current.Select(item => (IEnumerable<int>) new[] {item})) 
     .Select(index => index.ToArray()); 
    foreach (var index in indices) 
    { 
     var value = (TSource) source.GetValue(index); 
     result.SetValue(projection(value), index); 
    } 
    return result; 
} 

Phương pháp trên có thể được thử nghiệm với một loạt các cấp bậc 3 như sau:

var source = new int[2,3,4]; 

for (var i = source.GetLowerBound(0); i <= source.GetUpperBound(0); i++) 
    for (var j = source.GetLowerBound(1); j <= source.GetUpperBound(1); j++) 
     for (var k = source.GetLowerBound(2); k <= source.GetUpperBound(2); k++) 
      source[i, j, k] = i*100 + j*10 + k; 

var result = (int[,,]) source.ConvertAll<int, int>(i => i*i); 

for (var i = source.GetLowerBound(0); i <= source.GetUpperBound(0); i++) 
    for (var j = source.GetLowerBound(1); j <= source.GetUpperBound(1); j++) 
     for (var k = source.GetLowerBound(2); k <= source.GetUpperBound(2); k++) 
     { 
      var value = source[i, j, k]; 
      Debug.Assert(result[i, j, k] == value*value); 
     } 
+0

Ví dụ của bạn có lẽ nên sử dụng đại biểu thay vì chỉ định hàm trong lambda, để trả lời câu hỏi cụ thể hơn. –

+0

Rất gọn gàng. Điều này có tính đến mảng đa chiều không? – JustOnePixel

+0

À đúng rồi. Tôi đã tìm kiếm một phương pháp áp dụng hoặc một cái gì đó. Không bao giờ có thể nghĩ rằng nó được đặt tên là Chọn, nhưng tôi cho rằng nó đi cùng với cơ sở LINQ của tất cả các phương thức mở rộng thú vị. – jdmichal

5

Sử dụng System.Linq bạn có thể làm một cái gì đó như:

var newArray = arr.Select(x => myMethod(x)).ToArray(); 
2

truy vấn LINQ có thể dễ dàng giải quyết vấn đề này cho bạn - đảm bảo bạn đang tham chiếu System.Core.dll và có một số

using System.Linq; 

tuyên bố. Ví dụ, nếu bạn đã có mảng của bạn trong một biến có tên numberArray, đoạn code sau sẽ cung cấp cho bạn biết chính xác những gì bạn đang tìm kiếm:

var squares = numberArray.Select(n => n * n).ToArray(); 

Trận chung kết "ToArray" cuộc gọi chỉ cần thiết nếu bạn thực sự cần một mảng và không phải là một số điện thoại có thể truy cập được <int>.

+0

Tuyệt vời, cảm ơn. Điều này có tính đến mảng đa chiều không? – JustOnePixel

+0

Không - cho điều đó, bạn muốn có một lựa chọn lồng nhau, như numberArrays.Select (as => as.Select (n => n * n) .ToArray()). ToArray(). Đối với một cách tiếp cận dễ đọc hơn, chỉ cần sử dụng hai vòng lặp lồng nhau. – Ben

1

bạn có thể sử dụng LINQ để thực hiện điều này theo cách viết tắt nhưng hãy cẩn thận nhớ rằng một foreach xảy ra bên dưới anyway.

int[] x = {1,2,3}; 
x = x.Select((Y) => { return Y * Y; }).ToArray(); 
1

Đây là giải pháp khác cho mảng M x N, trong đó M và N không được biết tại thời gian biên dịch.

// credit: https://blogs.msdn.microsoft.com/ericlippert/2010/06/28/computing-a-cartesian-product-with-linq/ 
    public static IEnumerable<IEnumerable<T>> CartesianProduct<T>(IEnumerable<IEnumerable<T>> sequences) 
    { 
     IEnumerable<IEnumerable<T>> result = new[] { Enumerable.Empty<T>() }; 
     foreach (var sequence in sequences) 
     { 
      // got a warning about different compiler behavior 
      // accessing sequence in a closure 
      var s = sequence; 
      result = result.SelectMany(seq => s, (seq, item) => seq.Concat<T>(new[] { item })); 
     } 
     return result; 
    } 


    public static void ConvertInPlace(this Array array, Func<object, object> projection) 
    { 
     if (array == null) 
     { 
      return; 
     } 

     // build up the range for each dimension 
     var dimensions = Enumerable.Range(0, array.Rank).Select(r => Enumerable.Range(0, array.GetLength(r))); 

     // build up a list of all possible indices 
     var indexes = EnumerableHelper.CartesianProduct(dimensions).ToArray(); 

     foreach (var index in indexes) 
     { 
      var currentIndex = index.ToArray(); 
      array.SetValue(projection(array.GetValue(currentIndex)), currentIndex); 
     } 
    } 
+0

Thats có lẽ là một trong những tốt nhất tôi đã tìm thấy ... và tốt mà bạn đã cung cấp tín dụng cho các chàng trai đã viết nó (mặc dù các chàng hỏi rằng đã tức giận ra anh không áp dụng nó) –

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