Obtendo uma sub-matriz de uma matriz existente

Eu tenho uma matriz X de 10 elementos. Eu gostaria de criar uma nova matriz contendo todos os elementos do X que começam no índice 3 e terminam no índice 7. Claro que posso escrever facilmente um loop que vai fazer isso para mim, mas eu gostaria de manter meu código o mais limpo possível . Existe um método em c # que pode fazer isso por mim?

Algo como (pseudo código):

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

Array.Copy não se ajusta às minhas necessidades . Preciso que os itens da nova matriz sejam clones. Array.copy é apenas um equivalente de memcpy estilo memcpy , não é o que eu estou procurando.

Você poderia adicioná-lo como um método de extensão:

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

Atualize a re clonagem (o que não era óbvio na pergunta original). Se você realmente quer um clone 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); } } 

Isso requer que os objects sejam serializáveis ​​( [Serializable] ou ISerializable ), embora. Você poderia facilmente replace qualquer outro serializador conforme apropriado – XmlSerializer , DataContractSerializer , protobuf-net, etc.

Observe que clone profundo é complicado sem serialização; em particular, ICloneable é difícil de confiar na maioria dos casos.

Você pode usar o Array.Copy(...) para copiar para o novo array depois de criá-lo, mas não acho que exista um método que crie o novo array e copie um intervalo de elementos.

Se você estiver usando o .NET 3.5, você pode usar o LINQ:

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

mas isso será um pouco menos eficiente.

Veja esta resposta a uma pergunta semelhante para opções para situações mais específicas.

Você já pensou em usar o ArraySegment ?

http://msdn.microsoft.com/pt-br/library/1hsbd92d.aspx

Eu vejo que você quer fazer Clonagem, não apenas copiando referências. Nesse caso, você pode usar .Selecione para projetar membros da matriz para seus clones. Por exemplo, se seus elementos implementassem IClonable, você poderia fazer algo assim:

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

O código a seguir faz isso em uma linha:

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

Com base na resposta de Marc, mas adicionando o comportamento de clonagem desejado

 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 implementar o ICloneable é muito parecido com um trabalho duro, um reflexivo usando a biblioteca Copyable de Håvard Stranden para fazer o trabalho pesado necessário.

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

Observe que a implementação do OX.Copyable funciona com qualquer um dos seguintes:

Para a cópia automatizada funcionar, no entanto, uma das seguintes instruções deve conter, por exemplo:

  • Seu tipo deve ter um construtor sem parâmetros, ou
  • Deve ser um Copyable ou
  • Deve ter um IInstanceProvider registrado para seu tipo.

Então, isso deve cobrir quase qualquer situação que você tenha. Se você está clonando objects onde o sub-grafo contém coisas como conexões de database ou manipuladores de arquivo / stream, obviamente você tem problemas, mas isso é verdade para qualquer cópia profunda generalizada.

Se você quiser usar alguma outra abordagem de cópia profunda em vez disso, este artigo lista vários outros, então eu sugiro não tentar escrever o seu próprio.

Você pode fazer isso de forma bastante prática;

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

Eu acho que o código que você está procurando é:

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

Array.ConstrainedCopy irá funcionar.

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

Como uma alternativa para copiar os dados, você pode criar um wrapper que lhe dê access a uma parte da matriz original como se fosse uma cópia da parte da matriz. A vantagem é que você não obtém outra cópia dos dados na memory, e a desvantagem é uma pequena sobrecarga ao acessar os dados.

 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 

Não há um método único que faça o que você quer. Você precisará disponibilizar um método clone para a class em sua matriz. Então, se LINQ é uma opção:

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

Que tal usar o 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); 

Abaixo está o meu post original. Isso não vai funcionar

Você poderia 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 

Que tal agora:

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

Em seguida, você precisa implementar a interface ICloneable em todas as classs nas quais você precisa usar, mas isso deve ser feito.

Não tenho certeza de quão profundo é realmente, mas:

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

É um pouco sobrecarga, mas pode cortar um método desnecessário.

No que diz respeito à clonagem, não acho que a serialização chame seus construtores. Isso pode quebrar os invariantes de class se você estiver fazendo coisas interessantes no ctor’s.

Parece que a aposta mais segura são os methods de clonagem virtual que chamam construtores de cópia.

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

A clonagem de elementos em uma matriz não é algo que pode ser feito de maneira universal. Você quer clonagem profunda ou uma cópia simples de todos os membros?

Vamos para a abordagem do “melhor esforço”: clonar objects usando a interface ICloneable ou a serialização binária:

 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 não é uma solução perfeita, porque simplesmente não há nenhuma que funcione para qualquer tipo de object.

Você pode ter aula feita pela 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 depois

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

Este é o caminho ideal, eu encontrei, para fazer isso:

 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 que ajude!

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