c# - example - Obtener una sub-matriz de una matriz existente
multidimensional array c# (20)
¿Has considerado usar ArraySegment
?
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 haga por mí, pero me gustaría mantener mi código lo más limpio posible . ¿Hay un método en C # que pueda hacerlo por mí?
Algo como (pseudo código):
Array NewArray = oldArray.createNewArrayFromRange(int BeginIndex , int EndIndex)
Array.Copy
no se ajusta a mis necesidades . Necesito que los elementos de la nueva matriz sean clones. Array.copy
es solo un equivalente de C-Style memcpy
, no es lo que estoy buscando.
¿Qué hay de 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 se muestra mi post original. No funcionará
Podrías 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
Array.ConstrainedCopy funcionará.
public static void ConstrainedCopy (
Array sourceArray,
int sourceIndex,
Array destinationArray,
int destinationIndex,
int length
)
Basándose en la respuesta de Marc, pero añadiendo el comportamiento de clonación deseado
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;
}
Y si la implementación de ICloneable es demasiado como un trabajo duro, reflexivo, use la biblioteca de Håvard Stranden para realizar el trabajo pesado requerido.
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;
}
Tenga en cuenta que la implementación de OX.Copyable funciona con cualquiera de:
Sin embargo, para que la copia automatizada funcione, una de las siguientes declaraciones debe contener, por ejemplo:
- Su tipo debe tener un constructor sin parámetros, o
- Debe ser una copiable, o
- Debe tener un IInstanceProvider registrado para su tipo.
Así que esto debería cubrir casi cualquier situación que tenga. Si está clonando objetos en los que el gráfico secundario contiene elementos como conexiones de db o identificadores de archivos / flujos, obviamente tiene problemas, pero eso es cierto para cualquier copia profunda generalizada.
Si desea utilizar algún otro enfoque de copia profunda, este artículo enumera varios otros, así que le sugiero que no intente escribir el suyo.
Como alternativa a la copia de los datos, puede hacer una envoltura 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<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();
}
}
Uso:
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
Creo que el código que estás buscando es:
Array.Copy(oldArray, 0, newArray, BeginIndex, EndIndex - BeginIndex)
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<string>(Source).GetRange(2, 2).ToArray();
En lo que respecta a la clonación, no creo que la serialización llame a sus constructores. Esto puede romper 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 virtual que llaman a los constructores de copias.
protected MyDerivedClass(MyDerivedClass myClass)
{
...
}
public override MyBaseClass Clone()
{
return new MyDerivedClass(this);
}
Esta es la manera óptima, encontré, para hacer esto:
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]);
}
}
¡Espero eso ayude!
La clonación de elementos en una matriz no es algo que se pueda hacer de manera universal. ¿Quieres una clonación profunda o una copia simple de todos los miembros?
Vayamos por el enfoque del "mejor esfuerzo": clonando objetos usando la interfaz ICloneable o la serialización binaria:
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);
}
}
}
Esta no es una solución perfecta, porque simplemente no hay ninguna que funcione para cualquier tipo de objeto.
No estoy seguro de cuán profundo es realmente, pero:
MyArray.ToList<TSource>().GetRange(beginningIndex, endIndex).ToArray()
Es un poco de sobrecarga, pero puede cortar un método innecesario.
No hay un método único que haga lo que quieras. Deberá hacer que un método de clonación esté 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();
}
}
Podrías agregarlo como método de extensión:
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}
}
Actualización de la clonación (lo que no era obvio en la pregunta original). Si realmente quieres un clon profundo; algo como:
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);
}
}
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 la serialización; en particular, ICloneable
es difícil de confiar en la mayoría de los casos.
Puede usar Array.Copy(...)
para copiar en la nueva matriz después de Array.Copy(...)
creado, pero no creo que haya un método que cree la nueva matriz y copie un rango de elementos.
Si estás usando .NET 3.5, puedes 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.
Puedes tomar clases hechas por Microsoft:
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;
}
}
y entonces
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));
}
}
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;
}
A continuación, debe implementar la interfaz ICloneable en todas las clases en las que necesita usar esto, pero debería hacerlo.
Veo que quieres hacer Clonación, no solo copiar referencias. En este caso, puede usar .Seleccionar para proyectar miembros de la matriz a sus clones. Por ejemplo, si sus elementos implementados en IClonable podrían hacer algo como esto:
var newArray = array.Skip(3).Take(5).Select(eachElement => eachElement.Clone()).ToArray();
public static T[] SubArray<T>(T[] data, int index, int length)
{
List<T> retVal = new List<T>();
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();
}
string[] arr = { "Parrot" , "Snake" ,"Rabbit" , "Dog" , "cat" };
arr = arr.ToList().GetRange(0, arr.Length -1).ToArray();