Ottenere un sotto-array da un array esistente

Ho un array X di 10 elementi. Vorrei creare un nuovo array contenente tutti gli elementi di X che iniziano con l’indice 3 e terminano con l’indice 7. Certo, posso facilmente scrivere un ciclo che lo farà per me, ma vorrei mantenere il mio codice il più pulito ansible . C’è un metodo in C # che può farlo per me?

Qualcosa come (pseudo codice):

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

Array.Copy non si adatta alle mie esigenze . Ho bisogno che gli elementi nel nuovo array siano cloni. Array.copy è solo un equivalente memcpy stile C, non è quello che sto cercando.

Puoi aggiungerlo come metodo di estensione:

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

Aggiornamento della clonazione (che non era ovvio nella domanda originale). Se vuoi davvero un clone profondo; qualcosa di simile a:

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

Ciò richiede che gli oggetti siano serializzabili ( [Serializable] o ISerializable ), comunque. Potresti facilmente sostituire qualsiasi altro serializzatore come appropriato: XmlSerializer , DataContractSerializer , protobuf-net, ecc.

Nota che il clone profondo è complicato senza serializzazione; in particolare, ICloneable è difficile da fidarsi nella maggior parte dei casi.

È ansible utilizzare Array.Copy(...) per copiare nel nuovo array dopo averlo creato, ma non penso che esista un metodo che crea il nuovo array e copia un intervallo di elementi.

Se stai usando .NET 3.5, puoi usare LINQ:

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

ma sarà un po ‘meno efficiente.

Vedi questa risposta a una domanda simile per opzioni per situazioni più specifiche.

Hai preso in considerazione l’utilizzo di ArraySegment ?

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

Vedo che vuoi fare la clonazione, non solo copiare i riferimenti. In questo caso puoi usare. Seleziona per proiettare i membri dell’array ai loro cloni. Ad esempio, se i tuoi elementi hanno implementato IClonable potresti fare qualcosa del genere:

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

Il seguente codice lo fa in una riga:

 // 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(); 

Basandosi sulla risposta di Marc, ma aggiungendo il comportamento desiderato di clonazione

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

E se l'implementazione di ICloneable è troppo simile a quella del lavoro duro, è ansible utilizzare una libreria riflettente di Håvard Stranden per eseguire il pesante lavoro richiesto.

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

Si noti che l'implementazione di OX.Copyable funziona con uno di:

Perché la copia automatizzata funzioni, tuttavia, una delle seguenti affermazioni deve contenere per esempio:

  • Il suo tipo deve avere un costruttore senza parametri, o
  • Deve essere un Copiabile, o
  • Deve avere un IInstanceProvider registrato per il suo tipo.

Quindi questo dovrebbe coprire quasi tutte le situazioni che hai. Se stai clonando oggetti in cui il sotto-grafo contiene cose come connessioni db o handle di file / stream hai ovviamente dei problemi, ma è vero per qualsiasi deep copy generalizzata.

Se si desidera utilizzare un altro approccio di copia profonda, questo articolo ne elenca molti altri, quindi suggerirei di non provare a scriverne uno personale.

Puoi farlo abbastanza facilmente;

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

Penso che il codice che stai cercando sia:

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

Array.ConstrainedCopy funzionerà.

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

In alternativa alla copia dei dati, puoi creare un wrapper che ti consente di accedere a una parte dell’array originale come se fosse una copia della parte dell’array. Il vantaggio è che non si ottiene un’altra copia dei dati in memoria e l’inconveniente è un leggero sovraccarico quando si accede ai dati.

 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 

Non esiste un unico metodo che farà ciò che vuoi. Dovrai rendere disponibile un metodo clone per la class nel tuo array. Quindi, se LINQ è un’opzione:

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

Che ne dici di utilizzare 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); 

Di seguito è riportato il mio post originale. Non funzionerà

È ansible utilizzare 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 

Cosa ne pensi di questo:

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

È quindi necessario implementare l'interfaccia ICloneable su tutte le classi necessarie per l'utilizzo ma ciò dovrebbe essere fatto.

Non sono sicuro di quanto sia veramente profondo, ma:

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

È un po ‘sovraccarico, ma potrebbe tagliare un metodo non necessario.

Per quanto riguarda la clonazione, non penso che la serializzazione chiama i tuoi costruttori. Questo potrebbe violare gli invarianti di class se stai facendo cose interessanti nel ctor.

Sembra che la scommessa più sicura siano i metodi clone virtuali che chiamano i costruttori di copie.

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

La clonazione di elementi in un array non è qualcosa che può essere fatto in modo universale. Vuoi una clonazione profonda o una semplice copia di tutti i membri?

Andiamo per l’approccio “best effort”: clonazione di oggetti usando l’interfaccia ICloneable o serializzazione 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 

Questa non è una soluzione perfetta, perché semplicemente non c'è nessuno che funzioni per qualsiasi tipo di object.

Puoi prendere lezioni fatte da 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; } } 

e poi

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

Questo è il modo ottimale, ho trovato, per fare questo:

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

Spero che sia d'aiuto!

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