Obtener una sub-matriz de una matriz existente

Tengo una matriz X de 10 elementos. Me gustaría crear una nueva matriz que contenga todos los elementos de X que comienzan en el índice 3 y terminan en el índice 7. Claro que puedo escribir fácilmente un bucle que lo hará por mí, pero me gustaría mantener mi código lo más limpio posible. . ¿Hay algún método en C # que pueda hacerlo por mí?

Algo así como (pseudo código):

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

Array.Copy no se ajusta a mis necesidades . Necesito que los elementos en la nueva matriz sean clones. Array.copy es solo un equivalente memcpy C-Style, no es lo que estoy buscando.

Puede agregarlo como un método de extensión:

 public static T[] SubArray(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} } 

Actualizar re clonación (que no era obvio en la pregunta original). Si realmente quieres un clon profundo; algo como:

 public static T[] SubArrayDeepClone(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); } } 

Sin embargo, esto requiere que los objetos sean serializables ( [Serializable] o ISerializable ). Puede sustituir fácilmente cualquier otro serializador según corresponda: XmlSerializer , DataContractSerializer , protobuf-net, etc.

Tenga en cuenta que el clon profundo es complicado sin serialización; en particular, ICloneable es difícil de ICloneable en la mayoría de los casos.

Puede usar Array.Copy(...) para copiar en la nueva matriz después de que la haya creado, pero no creo que haya un método que cree la nueva matriz y copie un rango de elementos.

Si usa .NET 3.5, puede usar LINQ:

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

pero eso será algo menos eficiente.

Vea esta respuesta a una pregunta similar para opciones para situaciones más específicas.

¿Has considerado usar ArraySegment ?

http://msdn.microsoft.com/en-us/library/1hsbd92d.aspx

Veo que quieres hacer Clonación, no solo copiar referencias. En este caso, puede usar. Seleccione proyectar los miembros de la matriz a sus clones. Por ejemplo, si sus elementos implementados IClonable podría hacer algo como esto:

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

El siguiente código lo hace en una línea:

 // Source array string[] Source = new string[] { "A", "B", "C", "D" }; // Extracting a slice into another array string[] Slice = new List(Source).GetRange(2, 2).ToArray(); 
 string[] arr = { "Parrot" , "Snake" ,"Rabbit" , "Dog" , "cat" }; arr = arr.ToList().GetRange(0, arr.Length -1).ToArray(); 

Basándose en la respuesta de Marc, pero agregando el comportamiento de clonación deseado

 public static T[] CloneSubArray(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; } 

Y si la implementación de ICloneable se parece demasiado al trabajo arduo, uno reflexivo utilizando la biblioteca Copyable de Håvard Stranden para realizar el trabajo pesado requerido.

 using OX.Copyable; public static T[] DeepCopySubArray( 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; } 

Tenga en cuenta que la implementación de OX.Copyable funciona con cualquiera de:

Para que la copia automatizada funcione, una de las siguientes afirmaciones debe contener, por ejemplo:

  • Su tipo debe tener un constructor sin parámetros, o
  • Debe ser un Copyable, o
  • Debe tener un IInstanceProvider registrado para su tipo.

Entonces esto debería cubrir casi cualquier situación que tengas. Si está clonando objetos donde el gráfico secundario contiene elementos como conexiones db o manejadores de archivo / flujo, obviamente tiene problemas, pero es cierto para cualquier copia profunda generalizada.

Si desea utilizar algún otro enfoque de copia profunda en su lugar, este artículo enumera varios otros, por lo que le sugiero que no intente escribir el suyo propio.

Puedes hacer esto bastante fácil;

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

Creo que el código que estás buscando es:

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

Array.ConstrainedCopy funcionará.

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

Como alternativa a la copia de datos, puede crear un contenedor que le dé acceso a una parte de la matriz original como si fuera una copia de la parte de la matriz. La ventaja es que no obtiene otra copia de los datos en la memoria, y el inconveniente es una pequeña sobrecarga al acceder a los datos.

 public class SubArray : IEnumerable { 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 GetEnumerator() { for (int i = 0; i < Length; i++) { yield return _original[_start + i]; } } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } 

Uso:

 int[] original = { 1, 2, 3, 4, 5 }; SubArray copy = new SubArray(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 

No hay un solo método que haga lo que quieras. Tendrá que hacer un método de clonación disponible para la clase en su matriz. Entonces, si LINQ es una opción:

 Foo[] newArray = oldArray.Skip(3).Take(5).Select(item => item.Clone()).ToArray(); class Foo { public Foo Clone() { return (Foo)MemberwiseClone(); } } 

¿Qué hay de usar 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); 

A continuación está mi publicación original. No funcionará

Podría usar 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 

Qué tal esto:

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

Luego debe implementar la interfaz ICloneable en todas las clases en las que necesite usar esto pero eso debería hacerlo.

No estoy seguro de qué tan profundo es realmente, pero:

MyArray.ToList().GetRange(beginningIndex, endIndex).ToArray()

Es un poco caro, pero podría cortar un método innecesario.

En cuanto a la clonación, no creo que la serialización llame a sus constructores. Esto puede romper las invariantes de clase si estás haciendo cosas interesantes en el Ctor.

Parece que la apuesta más segura son los métodos de clonación virtuales que invocan a los constructores de copias.

 protected MyDerivedClass(MyDerivedClass myClass) { ... } public override MyBaseClass Clone() { return new MyDerivedClass(this); } 

Clonar elementos en una matriz no es algo que se pueda hacer de forma universal. ¿Quieres una clonación profunda o una copia simple de todos los miembros?

Veamos el enfoque del “mejor esfuerzo”: clonar objetos usando la interfaz ICloneable o la serialización binaria:

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

Esta no es una solución perfecta, porque simplemente no hay ninguna que funcione para ningún tipo de objeto.

Puedes tomar clases hechas por Microsoft:

 internal class Set { private int[] _buckets; private Slot[] _slots; private int _count; private int _freeList; private readonly IEqualityComparer _comparer; public Set() : this(null) { } public Set(IEqualityComparer comparer) { if (comparer == null) comparer = EqualityComparer.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; } } 

y entonces

 public static T[] GetSub(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> IntersectIteratorWithIndex(IEnumerable first, IEnumerable second) { var firstList = first.ToList(); var set = new Set(); foreach (var i in second) set.Add(i); foreach (var i in firstList) { if (set.Remove(i)) yield return new Tuple(i, firstList.IndexOf(i)); } } 

Esta es la forma óptima, encontré, para hacer esto:

 private void GetSubArrayThroughArraySegment() { int[] array = { 10, 20, 30 }; ArraySegment segment = new ArraySegment(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]); } } 

¡Espero eso ayude!

 public static T[] SubArray(T[] data, int index, int length) { List retVal = new List(); if (data == null || data.Length == 0) return retVal.ToArray(); bool startRead = false; int count = 0; for (int i = 0; i < data.Length; i++) { if (i == index && !startRead) startRead = true; if (startRead) { retVal.Add(data[i]); count++; if (count == length) break; } } return retVal.ToArray(); }