web-dev-qa-db-ja.com

既存の配列からサブ配列を取得する

私は10要素の配列Xを持っています。 Xからインデックス3で始まりインデックス7で終わるすべての要素を含む新しい配列を作成したいと思います。もちろん、それを実行するループを簡単に作成できますが、コードはできるだけきれいにしておきたい。私のためにそれを行うことができるC#の方法はありますか?

(擬似コード)のようなもの:

Array NewArray = oldArray.createNewArrayFromRange(int BeginIndex , int EndIndex)

Array.Copyは私のニーズに合いません。新しい配列のアイテムをクローンにする必要があります。 Array.copyは、Cスタイルのmemcpyと同等のものです。私が探しているものではありません。

292
user88637

あなたはそれを拡張メソッドとして追加することができます:

public static T[] SubArray<T>(this T[] data, int index, int length)
{
    T[] result = new T[length];
    Array.Copy(data, index, result, 0, length);
    return result;
}
static void Main()
{
    int[] data = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    int[] sub = data.SubArray(3, 4); // contains {3,4,5,6}
}

再クローニングを更新します(これは元の質問では明らかではありませんでした)。あなたが本当に深いクローンを欲しいなら。何かのようなもの:

public static T[] SubArrayDeepClone<T>(this T[] data, int index, int length)
{
    T[] arrCopy = new T[length];
    Array.Copy(data, index, arrCopy, 0, length);
    using (MemoryStream ms = new MemoryStream())
    {
        var bf = new BinaryFormatter();
        bf.Serialize(ms, arrCopy);
        ms.Position = 0;
        return (T[])bf.Deserialize(ms);
    }
}

ただし、これにはオブジェクトを直列化可能([Serializable]またはISerializable)にする必要があります。 XmlSerializerDataContractSerializer、protobuf-netなど、必要に応じて他のシリアライザを簡単に置き換えることができます。

ディープクローンはシリアライゼーションなしでは扱いにくいことに注意してください。特にICloneableはほとんどの場合信頼するのが難しいです。

421
Marc Gravell

新しい配列を作成した後にコピーするには、 Array.Copy(...) を使用できますが、新しい配列を作成する方法はないと思います(は要素の範囲をコピーします。

.NET 3.5を使用している場合は、LINQを使用できます

var newArray = array.Skip(3).Take(5).ToArray();

しかし、それはやや効率的ではありません。

より具体的な状況でのオプションについては、同様の質問に対する この回答 を参照してください。

280
Jon Skeet

ArraySegmentの使用を検討しましたか?

http://msdn.Microsoft.com/ja-jp/library/1hsbd92d.aspx

66
Alex Black

次のコードは1行でそれを行います。

// Source array
string[] Source = new string[] { "A", "B", "C", "D" };
// Extracting a slice into another array
string[] Slice = new List<string>(Source).GetRange(2, 2).ToArray();
31
Volker

参照をコピーするだけでなく、クローン作成を実行したいのですが。この場合、.Selectを使って、配列メンバーをそのクローンに投影することができます。例えば、あなたの要素がIClonableを実装しているなら、あなたはこのようなことをすることができます:

var newArray = array.Skip(3).Take(5).Select(eachElement => eachElement.Clone()).ToArray();

注:このソリューションには.NET Framework 3.5が必要です。

29
zvolkov
string[] arr = { "Parrot" , "Snake" ,"Rabbit" , "Dog" , "cat" };

arr = arr.ToList().GetRange(0, arr.Length -1).ToArray();
12
user3698437

Marcの答えを踏まえながら、望ましいクローン作成動作を追加する

public static T[] CloneSubArray<T>(this T[] data, int index, int length)
    where T : ICloneable
{
    T[] result = new T[length];
    for (int i = 0; i < length; i++)
    { 
        var original = data[index + i];
        if (original != null)
            result[i] = (T)original.Clone();            
    return result;
}

そして、ICloneableを実装するのがあまりにも大変な作業である場合は、 HåvardStrandenのCopyableライブラリ を使用して必要な重い作業を行うことができます。

using OX.Copyable;

public static T[] DeepCopySubArray<T>(
    this T[] data, int index, int length)
{
    T[] result = new T[length];
    for (int i = 0; i < length; i++)
    { 
        var original = data[index + i];
        if (original != null)
            result[i] = (T)original.Copy();            
    return result;
}

OX.Copyableの実装は以下のいずれかと連携することに注意してください。

ただし、自動コピーを機能させるには、たとえば次のいずれかのステートメントを満たす必要があります。

  • その型はパラメータのないコンストラクタを持たなければなりません。
  • コピー可能である必要があります。
  • その型に合わせてIInstanceProviderが登録されている必要があります。

だからこれはあなたが持っているほとんどすべての状況をカバーするはずです。サブグラフにdb接続やファイル/ストリームハンドルのようなものが含まれているオブジェクトをクローニングしている場合は明らかに問題がありますが、それは一般化されたディープコピーにも当てはまります。

代わりに他のディープコピーアプローチを使用したい場合は、この 記事に他のいくつかのリスト を記載してください。

8
ShuggyCoUk

これはかなり簡単に行えます。

    object[] foo = new object[10];
    object[] bar = new object[7];   
    Array.Copy(foo, 3, bar, 0, 7);  
8

探しているコードは次のようになります。

Array.Copy(oldArray, 0, newArray, BeginIndex, EndIndex - BeginIndex)

4
Sean

C#8では彼らは新しいRangeIndex型を導入しました

int[] a = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
Index i1 = 3;  // number 3 from beginning
Index i2 = ^4; // number 4 from end
var slice = a[i1..i2]; // { 3, 4, 5 }
3
Prasanth Louis

データをコピーする代わりに、元の配列の一部にアクセスして、あたかもそれが配列の一部のコピーであるかのようにアクセスできるラッパーを作成できます。利点は、データの別のコピーをメモリに取り込まないことです。また、データにアクセスするときのわずかなオーバーヘッドが欠点となります。

public class SubArray<T> : IEnumerable<T> {

   private T[] _original;
   private int _start;

   public SubArray(T[] original, int start, int len) {
      _original = original;
      _start = start;
      Length = len;
   }

   public T this[int index] {
      get {
         if (index < 0 || index >= Length) throw new IndexOutOfRangeException();
         return _original[_start + index];
      }
   }

   public int Length { get; private set; }

   public IEnumerator<T> GetEnumerator() {
      for (int i = 0; i < Length; i++) {
        yield return _original[_start + i];
      }
   }

   IEnumerator IEnumerable.GetEnumerator() {
      return GetEnumerator();
   }

}

使用法:

int[] original = { 1, 2, 3, 4, 5 };
SubArray<int> copy = new SubArray<int>(original, 2, 2);

Console.WriteLine(copy.Length); // shows: 2
Console.WriteLine(copy[0]); // shows: 3
foreach (int i in copy) Console.WriteLine(i); // shows 3 and 4
3
Guffa

Array.ConstrainedCopyは機能します。

public static void ConstrainedCopy (
    Array sourceArray,
    int sourceIndex,
    Array destinationArray,
    int destinationIndex,
    int length
)
2
crauscher

あなたが望むことをする方法は一つもありません。あなたはあなたの配列の中のクラスに対して利用可能なクローンメソッドを作る必要があるでしょう。次に、LINQがオプションの場合

Foo[] newArray = oldArray.Skip(3).Take(5).Select(item => item.Clone()).ToArray();

class Foo
{
    public Foo Clone()
    {
        return (Foo)MemberwiseClone();
    }
}
1
Thorarin

私はそれが本当にどれほど深いのかわからないが、:

MyArray.ToList<TSource>().GetRange(beginningIndex, endIndex).ToArray()

多少のオーバーヘッドがありますが、不要な方法を削除する可能性があります。

1
SCNerd

これはどう:

public T[] CloneCopy(T[] array, int startIndex, int endIndex) where T : ICloneable
{
    T[] retArray = new T[endIndex - startIndex];
    for (int i = startIndex; i < endIndex; i++)
    {
        array[i - startIndex] = array[i].Clone();
    }
    return retArray;

}

それから、これを使用する必要があるすべてのクラスにICloneableインターフェースを実装する必要がありますが、それでうまくいくはずです。

1
RCIX

Array.ConstrainedCopy を使用してはどうですか。

int[] ArrayOne = new int[8] {1,2,3,4,5,6,7,8};
int[] ArrayTwo = new int[5];
Array.ConstrainedCopy(ArrayOne, 3, ArrayTwo, 0, 7-3);

以下が私の最初の投稿です。うまくいかないだろう

Array.CopyTo を使用できます。

int[] ArrayOne = new int[8] {1,2,3,4,5,6,7,8};
int[] ArrayTwo = new int[5];
ArrayOne.CopyTo(ArrayTwo,3); //starts copy at index=3 until it reaches end of
                             //either array
1
Mike

これが最適な方法です。

private void GetSubArrayThroughArraySegment() {
  int[] array = { 10, 20, 30 };
  ArraySegment<int> segment = new ArraySegment<int>(array,  1, 2);
  Console.WriteLine("-- Array --");
  int[] original = segment.Array;
  foreach (int value in original)
  {
    Console.WriteLine(value);
  }
  Console.WriteLine("-- Offset --");
  Console.WriteLine(segment.Offset);
  Console.WriteLine("-- Count --");
  Console.WriteLine(segment.Count);

  Console.WriteLine("-- Range --");
  for (int i = segment.Offset; i <= segment.Count; i++)
  {
    Console.WriteLine(segment.Array[i]);
  }
}

それが役に立てば幸い!

0
OscarMas

マイクロソフト製の授業を受けることができます:

internal class Set<TElement>
{
    private int[] _buckets;
    private Slot[] _slots;
    private int _count;
    private int _freeList;
    private readonly IEqualityComparer<TElement> _comparer;

    public Set()
        : this(null)
    {
    }

    public Set(IEqualityComparer<TElement> comparer)
    {
        if (comparer == null)
            comparer = EqualityComparer<TElement>.Default;
        _comparer = comparer;
        _buckets = new int[7];
        _slots = new Slot[7];
        _freeList = -1;
    }

    public bool Add(TElement value)
    {
        return !Find(value, true);
    }

    public bool Contains(TElement value)
    {
        return Find(value, false);
    }

    public bool Remove(TElement value)
    {
        var hashCode = InternalGetHashCode(value);
        var index1 = hashCode % _buckets.Length;
        var index2 = -1;
        for (var index3 = _buckets[index1] - 1; index3 >= 0; index3 = _slots[index3].Next)
        {
            if (_slots[index3].HashCode == hashCode && _comparer.Equals(_slots[index3].Value, value))
            {
                if (index2 < 0)
                    _buckets[index1] = _slots[index3].Next + 1;
                else
                    _slots[index2].Next = _slots[index3].Next;
                _slots[index3].HashCode = -1;
                _slots[index3].Value = default(TElement);
                _slots[index3].Next = _freeList;
                _freeList = index3;
                return true;
            }
            index2 = index3;
        }
        return false;
    }

    private bool Find(TElement value, bool add)
    {
        var hashCode = InternalGetHashCode(value);
        for (var index = _buckets[hashCode % _buckets.Length] - 1; index >= 0; index = _slots[index].Next)
        {
            if (_slots[index].HashCode == hashCode && _comparer.Equals(_slots[index].Value, value))
                return true;
        }
        if (add)
        {
            int index1;
            if (_freeList >= 0)
            {
                index1 = _freeList;
                _freeList = _slots[index1].Next;
            }
            else
            {
                if (_count == _slots.Length)
                    Resize();
                index1 = _count;
                ++_count;
            }
            int index2 = hashCode % _buckets.Length;
            _slots[index1].HashCode = hashCode;
            _slots[index1].Value = value;
            _slots[index1].Next = _buckets[index2] - 1;
            _buckets[index2] = index1 + 1;
        }
        return false;
    }

    private void Resize()
    {
        var length = checked(_count * 2 + 1);
        var numArray = new int[length];
        var slotArray = new Slot[length];
        Array.Copy(_slots, 0, slotArray, 0, _count);
        for (var index1 = 0; index1 < _count; ++index1)
        {
            int index2 = slotArray[index1].HashCode % length;
            slotArray[index1].Next = numArray[index2] - 1;
            numArray[index2] = index1 + 1;
        }
        _buckets = numArray;
        _slots = slotArray;
    }

    internal int InternalGetHashCode(TElement value)
    {
        if (value != null)
            return _comparer.GetHashCode(value) & int.MaxValue;
        return 0;
    }

    internal struct Slot
    {
        internal int HashCode;
        internal TElement Value;
        internal int Next;
    }
}

その後

public static T[] GetSub<T>(this T[] first, T[] second)
    {
        var items = IntersectIteratorWithIndex(first, second);
        if (!items.Any()) return new T[] { };


        var index = items.First().Item2;
        var length = first.Count() - index;
        var subArray = new T[length];
        Array.Copy(first, index, subArray, 0, length);
        return subArray;
    }

    private static IEnumerable<Tuple<T, Int32>> IntersectIteratorWithIndex<T>(IEnumerable<T> first, IEnumerable<T> second)
    {
        var firstList = first.ToList();
        var set = new Set<T>();
        foreach (var i in second)
            set.Add(i);
        foreach (var i in firstList)
        {
            if (set.Remove(i))
                yield return new Tuple<T, Int32>(i, firstList.IndexOf(i));
        }
    }
0
Smagin Alexey

クローン作成に関しては、シリアライゼーションがあなたのコンストラクタを呼び出すとは思わない。あなたがコントロールの中で面白いことをしているなら、これはクラスの不変式を壊すかもしれません。

より安全な方法は、コピーコンストラクタを呼び出す仮想クローンメソッドです。

protected MyDerivedClass(MyDerivedClass myClass) 
{
  ...
}

public override MyBaseClass Clone()
{
  return new MyDerivedClass(this);
}
0
Hans Malherbe

配列内の要素を複製することは、普遍的な方法でできることではありません。あなたはディープクローニングや全メンバーの簡単なコピーが欲しいですか?

「ベストエフォート」アプローチ、つまりICloneableインターフェイスまたはバイナリシリアル化を使用したオブジェクトのクローン作成に進みましょう。

public static class ArrayExtensions
{
  public static T[] SubArray<T>(this T[] array, int index, int length)
  {
    T[] result = new T[length];

    for (int i=index;i<length+index && i<array.Length;i++)
    {
       if (array[i] is ICloneable)
          result[i-index] = (T) ((ICloneable)array[i]).Clone();
       else
          result[i-index] = (T) CloneObject(array[i]);
    }

    return result;
  }

  private static object CloneObject(object obj)
  {
    BinaryFormatter formatter = new BinaryFormatter();

    using (MemoryStream stream = new MemoryStream())
    {
      formatter.Serialize(stream, obj);

      stream.Seek(0,SeekOrigin.Begin);

      return formatter.Deserialize(stream);
    }
  }
}

これは完璧な解決策ではありません。どんな種類のオブジェクトに対してもうまくいくものがないからです。

0

拡張方法を使う:

public static T[] Slice<T>(this T[] source, int start, int end)
    {
        // Handles negative ends.
        if (end < 0)
        {
            end = source.Length + end;
        }
        int len = end - start;

        // Return new array.
        T[] res = new T[len];
        for (int i = 0; i < len; i++)
        {
            res[i] = source[i + start];
        }
        return res;
    }

そしてあなたはそれを使うことができます

var NewArray = OldArray.Slice(3,7);
0
Erwin Draconis