C#:更改数组中每个项的值

我想知道是否有内置的.NETfunction来根据提供的委托的结果更改数组中的每个值。 例如,如果我有一个数组{1,2,3}和一个返回每个值的平方的委托,我希望能够运行一个接受数组和委托的方法,并返回{1,4,9} 。 有这样的事情吗?

不是我知道(替换每个元素而不是转换为新的数组或序列),但它非常容易编写:

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

使用:

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

当然,如果您不需要更改现有arrays,则使用Select发布的其他答案将更具function性。 或者.NET 2中现有的ConvertAll方法:

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

这都是假设一维数组。 如果你想包括矩形数组,它会变得更加棘手,特别是如果你想避免拳击。

LINQ使用Select扩展方法为投影提供支持:

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

您还可以使用稍微不那么流畅的Array.ConvertAll方法:

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

可以通过嵌套投影来处理锯齿状数组:

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

多维数组稍微复杂一些。 我编写了以下扩展方法,它将多维数组中的每个元素都投影,无论它的等级如何。

 static Array ConvertAll(this Array source, Converter 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>) null, (total, current) => total != null ? total.SelectMany( item => current, (existing, item) => existing.Concat(new[] {item})) : current.Select(item => (IEnumerable) new[] {item})) .Select(index => index.ToArray()); foreach (var index in indices) { var value = (TSource) source.GetValue(index); result.SetValue(projection(value), index); } return result; } 

上述方法可以使用等级3的数组进行测试,如下所示:

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

使用System.Linq你可以做类似的事情:

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

LINQ查询可以轻松地为您解决此问题 – 确保您引用System.Core.dll并拥有

 using System.Linq; 

声明。 例如,如果您的数组位于名为numberArray的变量中,则以下代码将为您提供您正在寻找的内容:

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

只有在您确实需要一个数组而不是IEnumerable 时才需要最后的“ToArray”调用。

您可以使用linq以速记方式完成此操作,但要小心记住,无论如何都会在下面发生foreach。

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

这是M x Narrays的另一种解决方案,其中M和N在编译时是未知的。

  // credit: https://blogs.msdn.microsoft.com/ericlippert/2010/06/28/computing-a-cartesian-product-with-linq/ public static IEnumerable> CartesianProduct(IEnumerable> sequences) { IEnumerable> result = new[] { Enumerable.Empty() }; 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(new[] { item })); } return result; } public static void ConvertInPlace(this Array array, Func 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); } }