提供されたデリゲートの結果に基づいて配列の各値を変更する組み込みの.NET機能があるかどうか疑問に思います。たとえば、配列{1,2,3}
と、各値の2乗を返すデリゲートがある場合、配列とデリゲートを受け取り、{1,4,9}
を返すメソッドを実行できるようにしたいと思います。このようなものはすでに存在しますか?
私が知っていることではありませんが(新しい配列やシーケンスに変換するのではなく、各要素を置き換える)、非常に簡単に書くことができます。
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]);
}
}
使用する:
int[] values = { 1, 2, 3 };
values.ConvertInPlace(x => x * x);
もちろん、既存の配列を実際に変更する必要がない場合、Select
を使用して投稿された他の回答の方が機能的です。または、.NET2の既存のConvertAll
メソッド:
int[] values = { 1, 2, 3 };
values = Array.ConvertAll(values, x => x * x);
これはすべて、1次元配列を想定しています。長方形の配列を含めたい場合、特にボクシングを避けたい場合は、注意が必要です。
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<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;
}
上記の方法は、次のようにランク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);
}
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();
最後の「ToArray」呼び出しは、IEnumerable <int>ではなく、実際に配列が必要な場合にのみ必要です。
これは、コンパイル時にMとNが不明なM xN配列の別のソリューションです。
// 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);
}
}
linqを使用してこれを簡単に実行できますが、とにかくforeachがその下で発生することに注意してください。
int[] x = {1,2,3};
x = x.Select(( Y ) => { return Y * Y; }).ToArray();