How do I clone a range of array elements to a new array?
Solution 1
You could add it as an extension method:
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}
}
Update re cloning (which wasn't obvious in the original question). If you really want a deep clone; something like:
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);
}
}
This does require the objects to be serializable ([Serializable]
or ISerializable
), though. You could easily substitute for any other serializer as appropriate - XmlSerializer
, DataContractSerializer
, protobuf-net, etc.
Note that deep clone is tricky without serialization; in particular, ICloneable
is hard to trust in most cases.
Solution 2
You can use Array.Copy(...)
to copy into the new array after you've created it, but I don't think there's a method which creates the new array and copies a range of elements.
If you're using .NET 3.5 you could use LINQ:
var newArray = array.Skip(3).Take(5).ToArray();
but that will be somewhat less efficient.
See this answer to a similar question for options for more specific situations.
Solution 3
Have you considered using ArraySegment
?
http://msdn.microsoft.com/en-us/library/1hsbd92d.aspx
Solution 4
I see you want to do Cloning, not just copying references.
In this case you can use .Select
to project array members to their clones.
For example, if your elements implemented IClonable
you could do something like this:
var newArray = array.Skip(3).Take(5).Select(eachElement => eachElement.Clone()).ToArray();
Note: This solution requires .NET Framework 3.5.
Solution 5
The following code does it in one line:
// 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();
user88637
Updated on July 08, 2022Comments
-
user88637 almost 2 years
I have an array X of 10 elements. I would like to create a new array containing all the elements from X that begin at index 3 and ends in index 7. Sure I can easily write a loop that will do it for me but I would like to keep my code as clean as possible. Is there a method in C# that can do it for me?
Something like (pseudo code):
Array NewArray = oldArray.createNewArrayFromRange(int BeginIndex , int EndIndex)
Array.Copy
doesn't fit my needs. I need the items in the new array to be clones.Array.copy
is just a C-Stylememcpy
equivalent, it's not what I'm looking for. -
Marc Gravell almost 15 yearsThat just copies the data; it won't create the new array etc; and if the array is new, we could use Array.Copy which is more efficient (no need for the additional checks/rollbacks).
-
crauscher almost 15 yearsThats right, but creating a new Array is only one line of code and no new method is required. I agree that Array.Copy will work as well.
-
Ian Roke almost 15 years+1 I like this variation too. Jon, can you expand on why this is deemed less efficient?
-
Marc Gravell almost 15 years@Jon: To match the question, wouldn't that be "Take(5)"? @Ian: the Array.Copy approach doesn't involve an enumerator, and will most-likely be a straight memcopy...
-
Jon Skeet almost 15 years@Ian: The LINQ approach introduces two levels of indirection (the iterators), has to explicitly skip over items, and doesn't know how big the final array is going to be beforehand. Consider taking the second half of a two-million-element array: a simple "create target array, copy" approach will just copy the required block without touching the other elements, and in one go. The LINQ approach will walk through the array until it reaches the start point, then start taking values, building up a buffer (increasing the buffer size and copying periodically). Much less efficient.
-
user88637 almost 15 yearswhat does Array.Copy does internally . is it shallow copy ? C-Style memcopy?
-
Lasse V. Karlsen almost 15 yearsYes, it is a memcopy-type copy.
-
user88637 almost 15 yearsSo what you are actually telling me is that Array.Copy is good only for primitives.
-
Marc Gravell almost 15 yearsNo... just that with classes it'll copy the reference - it won't create a new instance.
-
user88637 almost 15 yearsbut what if I want my references to be cloned?
-
Marc Gravell almost 15 yearsThen... tough; it doesn't do that.... you'd probably need to use serialization to achieve something similar
-
Zr40 almost 15 yearsNo, bar will still be null. Array.Copy doesn't magically create a new array, especially since bar isn't passed with ref or out.
-
ShuggyCoUk almost 15 yearssee my answer for some alternates and a link to several implementations. the part about doing it to a sub array is really rather trivial, what you really want is the cloning bit and that's a complex and somewhat open question which depends entirely on your expectations of what 'correct' behaviour should be.
-
ShuggyCoUk almost 15 yearsYes I noted the null check but didn't want to confuse the OP in case he didn't read the source.
-
Donald Byrd almost 15 yearsShoudn't that be something like result[i-index] = (T)... ?
-
Stan R. almost 15 yearsthis is a pretty creative way of doing a deep copy, very nice
-
Philippe Leybaert almost 15 yearsyes :) And not only that. The loop boundary is wrong. I'll fix it. Thanks!
-
Marcus Griep almost 15 yearsThis is nice. And it especially good to point out that ICloneable is unreliable, because oh, is it ever.
-
RandomNickName42 almost 15 yearsI think I've been making some good friend's here.... same answer as you ;) and I got voted down plenty!! hah!! Anyhow, good times good times.
-
Marc Gravell almost 15 yearsWhether serialization calls your constructors is up to the specific serializer. Some do, some don't. But those that don't typically offer callback support to allow you to do any fixups required.
-
Hans Malherbe almost 15 yearsThis highlights another friction point of serialization: You have to provide default constructors.
-
Robert P over 14 yearsFantastic! If this does what I think it does, this solves my problem exactly.
-
Guffa over 14 years@Robert: No, it's not. Try to use an ArraySegment instead, and you see than you can neither access the items by index, not iterate throught the items.
-
Alex Black over 14 yearsIt probably does what you want, but it doesn't support the default array syntax, nor does it support IEnumerable, so its not especially clean.
-
Håvard S over 14 yearsJust a sidenote: The latest version of Copyable on GitHub does not require objects to have a parameterless constructor. :) See github.com/havard/copyable
-
Popplars about 13 yearsThanks for underlining the problems with deep cloning in C#. It's a shame really, as deep copying is a fundamental operation.
-
nawfal over 11 yearsThis needs more upvote. In my own exp, ArraySegment copying is slightly faster too (after all I use arrays for speed critical stuffs)..
-
Ales Potocnik Hahonina over 10 yearsThanks! Works like a charm for most common needs.
-
p.s.w.g over 10 years@AlexBlack It looks like as of .NET 4.5, it implements
IEnumerable<T>
and a variety of other useful interfaces. -
smwikipedia about 10 yearsThis is more elegant.
-
Klaus78 over 9 yearsif 5 is the EndIndexm, then the correct question is array.Skip(3).Take(5-3+1).ToArray(); ie. array.Skip(StartIndex).Take(EndIndex-StartIndex+1).ToArray();
-
dragonfly02 over 9 yearsI totally agree with @nawfal, this does need more up voting and it seems that the language designers realized this is a common task now. It works just expected!
-
Exectron about 9 yearsHow would you use
ArraySegment
to answer the original question? -
Dimitris almost 9 yearsSinge line and no need to add Linq. It's my preferred way.
-
nikodaemus almost 9 years@CraigMcQueen - Try the following single-line approach:
IList<T> newArray = (IList<T>)new ArraySegment<T>(oldArray, beginIndex, endIndex);
-
I.G. Pascual about 8 yearsStill it doesn't clone the source... but it's a good approach anyway
-
Massimo over 7 yearsDoes it fit .net 2 ? I got errors about missing System.Core dll.
-
Krauss over 7 yearsIt should clone the source because ToArray: (1) creates a new array and (2) executes Array.Copy. In the end Source and Slice are two separate objects. The approach is correct, however, I prefer Array.Copy: referencesource.microsoft.com/#mscorlib/system/collections/…
-
Mike over 6 yearsThis is exactly what I was looking for. This works for any
IEnumerable
. I can get anIEnumerable
,IList
,IArray
, etc... with minimal fuss, inline if I need to. If I don't need the deep copy, I just remove theSelect
. DroppingSkip
orTake
allows me to control the range. Alternatively, I can mix it up withSkipWhile
and/orTakeWhile
. -
Mike over 6 years@CraigMcQueen - Since the OP asked for an array, you would use
var newArray = new ArraySegment(oldArray, beginIndex, endIndex - beginIndex).ToArray()
. The previous comment will give you a list (i.e. not an array) that has too many elements, since it is using endIndex where a count is expected. -
Mike over 6 yearsThis approach is only more performant if all of the following are true: you do not need a deep copy, you do not need an array and you are planning to use an enumerator to traverse the elements. Otherwise you will need multiple traversals, i.e. you are forced to do something like
new ArraySegment(oldArray, offset, count).Select(e => e.Clone()).ToArray()
. The approach given by @zvolkov offers a lot of flexibility, and if you can use a list instead of an array, then the approach by @eeerahul has good performance (even better if deep copy isn't required). -
Mike over 6 yearsEGADS! Let's start with it only works if everything being serialized is
[Serializable]
. If you have a very complex object, you need to ask yourself why you need the deep copy. Otherwise, consider finding a way to usestruct
instead ofclass
, soArray.Copy
can work. Alternatively, implementICloneable
or implement some other clone/deep copy method on the objects and only clone/copy on use. -
Aaron Franke over 6 yearserror: The name
Array
does not exist in the current context. -
Marc Gravell over 6 years@AaronFranke I don't include common using directives in most code samples. Add
using System;
and it should work -
Neeraj Singh Chouhan over 4 yearsArraySegment does not support clear or remove element types methods
-
Nomnom almost 3 yearsThis should be the approved answer now.
-
Tinister almost 2 years
.Clone()
isn't necessary. The range indexer already creates a fresh array instance.