C#: Altering values for every item in an array

前端 未结 6 1294
醉梦人生
醉梦人生 2020-12-30 01:54

I\'m wondering if there is built-in .NET functionality to change each value in an array based on the result of a provided delegate. For example, if I had an array {1,

相关标签:
6条回答
  • 2020-12-30 02:27

    LINQ provides support for projections using the Select extension method:

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

    You can also use the slightly less fluent Array.ConvertAll method:

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

    Jagged arrays can be processed by nesting the projections:

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

    Multidimensional arrays are a little more complex. I've written the following extension method which projects every element in a multidimensional array no matter what its rank.

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

    The above method can be tested with an array of rank 3 as follows:

    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 讨论(0)
  • 2020-12-30 02:33

    you can use linq to accomplish this in shorthand but be careful remember that a foreach occurs underneath anyway.

    int[] x =  {1,2,3};
    x = x.Select(( Y ) => { return Y * Y; }).ToArray();
    
    0 讨论(0)
  • 2020-12-30 02:34

    LINQ queries could easily solve this for you - make sure you're referencing System.Core.dll and have a

    using System.Linq;
    

    statement. For example, if you had your array in a variable named numberArray, the following code would give you exactly what you're looking for:

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

    The final "ToArray" call is only needed if you actually need an array, and not an IEnumerable<int>.

    0 讨论(0)
  • 2020-12-30 02:35

    Using System.Linq you could do something like:

    var newArray = arr.Select(x => myMethod(x)).ToArray();
    
    0 讨论(0)
  • 2020-12-30 02:36

    Here is another solution for M x N arrays, where M and N are not known at compile time.

        // 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 讨论(0)
  • 2020-12-30 02:45

    Not that I'm aware of (replacing each element rather than converting to a new array or sequence), but it's incredibly easy to write:

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

    Use:

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

    Of course if you don't really need to change the existing array, the other answers posted using Select would be more functional. Or the existing ConvertAll method from .NET 2:

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

    This is all assuming a single-dimensional array. If you want to include rectangular arrays, it gets trickier, particularly if you want to avoid boxing.

    0 讨论(0)
提交回复
热议问题