Obtenir un sous-tableau à partir d’un tableau existant

J’ai un tableau X de 10 éléments. Je voudrais créer un nouveau tableau contenant tous les éléments de X qui commencent à l’index 3 et se termine dans l’index 7. Bien sûr, je peux facilement écrire une boucle qui le fera pour moi mais je voudrais garder mon code aussi propre que possible . Y a-t-il une méthode en C # qui peut le faire pour moi?

Quelque chose comme (pseudo code):

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

Array.Copy ne correspond pas à mes besoins . J’ai besoin que les éléments du nouveau tableau soient des clones. Array.copy est juste un équivalent de style C-Style, ce n’est pas ce que je cherche.

Vous pouvez l’append comme méthode d’extension:

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

Mise à jour sur le clonage (ce qui n’était pas évident dans la question initiale). Si vous voulez vraiment un clone profond; quelque chose comme:

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

Cela nécessite toutefois que les objects soient sérialisables ( [Serializable] ou ISerializable ). Vous pouvez facilement remplacer tout autre sérialiseur approprié – XmlSerializer , DataContractSerializer , protobuf-net, etc.

Notez que le clone profond est délicat sans sérialisation; En particulier, ICloneable est difficile à faire confiance dans la plupart des cas.

Vous pouvez utiliser Array.Copy(...) pour copier dans le nouveau tableau après l’avoir créé, mais je ne pense pas qu’il existe une méthode qui crée le nouveau tableau et copie une gamme d’éléments.

Si vous utilisez .NET 3.5, vous pouvez utiliser LINQ:

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

mais ce sera un peu moins efficace.

Voir cette réponse à une question similaire pour des options pour des situations plus spécifiques.

Avez-vous envisagé d’utiliser ArraySegment ?

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

Je vois que vous voulez faire du clonage, pas simplement copier des références. Dans ce cas, vous pouvez utiliser .Select pour projeter les membres du tableau à leurs clones. Par exemple, si vos éléments implémentaient IClonable, vous pourriez faire quelque chose comme ceci:

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

Le code suivant le fait en une seule ligne:

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

S’appuyant sur la réponse de Marc mais en ajoutant le comportement de clonage souhaité

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

Et si l'implémentation d'ICloneable est trop difficile, il est nécessaire de réfléchir à l'aide de la bibliothèque Copyable de Håvard Stranden pour faire le gros du travail.

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

Notez que l'implémentation OX.Copyable fonctionne avec l'un des éléments suivants:

Cependant, pour que la copie automatisée fonctionne, l'une des instructions suivantes doit être conservée:

  • Son type doit avoir un constructeur sans paramètre, ou
  • Ce doit être un Copyable, ou
  • Il doit avoir un IInstanceProvider enregistré pour son type.

Donc, cela devrait couvrir presque toutes les situations que vous avez. Si vous clonez des objects dont le sous-graphique contient des éléments tels que des connexions à la firebase database ou des descripteurs de fichiers / stream, vous avez évidemment des problèmes, mais cela est vrai pour toute copie profonde généralisée.

Si vous souhaitez utiliser une autre approche de copie profonde, cet article en énumère plusieurs autres . Je suggère donc de ne pas essayer d’écrire le vôtre.

Vous pouvez le faire assez facilement;

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

Je pense que le code que vous recherchez est:

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

Array.ConstrainedCopy fonctionnera.

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

Au lieu de copier les données, vous pouvez créer un wrapper qui vous donne access à une partie du tableau d’origine comme s’il s’agissait d’une copie de la partie du tableau. L’avantage est que vous n’obtenez pas une autre copie des données en mémoire, et l’inconvénient est une légère surcharge lors de l’access aux données.

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

Usage:

 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 

Il n’y a pas de méthode unique qui fera ce que vous voulez. Vous devrez créer une méthode de clonage pour la classe de votre tableau. Ensuite, si LINQ est une option:

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

Que diriez-vous d’ utiliser 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); 

Ci-dessous est mon post original. Ça ne marchera pas

Vous pouvez utiliser 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 

Que dis-tu de ça:

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

Vous devez ensuite implémenter l'interface ICloneable sur toutes les classes dont vous avez besoin pour l'utiliser, mais cela devrait se faire.

Je ne suis pas sûr de sa profondeur mais:

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

C’est un peu frais, mais cela pourrait éliminer une méthode inutile.

En ce qui concerne le clonage, je ne pense pas que la sérialisation appelle vos constructeurs. Cela peut casser les invariants de classe si vous faites des choses intéressantes dans le ctor.

Il semble que le pari le plus sûr soit les méthodes de clone virtuel appelant les constructeurs de copie.

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

Le clonage d’éléments dans un tableau n’est pas quelque chose qui peut être fait de manière universelle. Voulez-vous un clonage profond ou une simple copie de tous les membres?

Allons-y pour l’approche du “meilleur effort”: cloner des objects en utilisant l’interface ICloneable ou la sérialisation binary:

 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 

Ce n'est pas une solution parfaite, car il n'y en a simplement aucune qui fonctionnera pour tout type d'object.

Vous pouvez prendre des cours faits par 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; } } 

et alors

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

C’est la manière optimale, j’ai trouvé, pour faire ceci:

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

J'espère que cela aide!

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