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

Jus*_*xel 17 c# linq arrays delegates projection

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

Nat*_*lch 31

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

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

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

var squares = Array.ConvertAll(numbers, i => i*i);
Run Code Online (Sandbox Code Playgroud)

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

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

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

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;
}
Run Code Online (Sandbox Code Playgroud)

上述方法可以使用等级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<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);
        }
Run Code Online (Sandbox Code Playgroud)


Jon*_*eet 22

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

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]);
    }
}
Run Code Online (Sandbox Code Playgroud)

使用:

int[] values = { 1, 2, 3 };
values.ConvertInPlace(x => x * x);
Run Code Online (Sandbox Code Playgroud)

当然,如果您不需要更改现有阵列,则使用的其他答案Select将更具功能性.或者ConvertAll.NET 2中的现有方法:

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

这都是假设一维数组.如果你想要包含矩形数组,它会变得更加棘手,特别是如果你想避免装箱.

  • 我不明白为什么这会击败内森回答接受的答案.我错过了什么?选择完全符合他的需要,不是吗? (2认同)

hac*_*sid 5

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

var newArray = arr.Select(x => myMethod(x)).ToArray();
Run Code Online (Sandbox Code Playgroud)