How do I clone a range of array elements to a new array?
Asked Answered
J

26

388

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-Style memcpy equivalent, it's not what I'm looking for.

Juvenile answered 3/6, 2009 at 8:26 Comment(5)
So, the two lines that it would take to declare the new array and call .Copy( ) is not "clean code"?Sharonsharona
@Ed Swangren - not if you need to do it in the middle of a chained expression, no ;-pAesculapian
ShaggyUk's answer is probably the correct one: #944135Psephology
See a solution on dotnetperls.com/array-sliceRevolt
If a view of the original array is what you want (no copying of elements) you might like ArraySlice<T>: github.com/henon/SliceAndDiceVershen
A
507

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.

Aesculapian answered 3/6, 2009 at 8:32 Comment(12)
what does Array.Copy does internally . is it shallow copy ? C-Style memcopy?Juvenile
Yes, it is a memcopy-type copy.Scend
So what you are actually telling me is that Array.Copy is good only for primitives.Juvenile
No... just that with classes it'll copy the reference - it won't create a new instance.Aesculapian
but what if I want my references to be cloned?Juvenile
Then... tough; it doesn't do that.... you'd probably need to use serialization to achieve something similarAesculapian
see 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.Choiseul
Thanks for underlining the problems with deep cloning in C#. It's a shame really, as deep copying is a fundamental operation.Lonnie
Does it fit .net 2 ? I got errors about missing System.Core dll.Farm
EGADS! 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 use struct instead of class, so Array.Copy can work. Alternatively, implement ICloneable or implement some other clone/deep copy method on the objects and only clone/copy on use.Tanberg
error: The name Array does not exist in the current context.Meng
@AaronFranke I don't include common using directives in most code samples. Add using System; and it should workAesculapian
E
350

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.

Exurbanite answered 3/6, 2009 at 8:31 Comment(5)
+1 I like this variation too. Jon, can you expand on why this is deemed less efficient?Yarmouth
@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...Aesculapian
@Marc: Yes indeed. Too much question skimming :)Exurbanite
@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.Exurbanite
if 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();Unutterable
P
77

Have you considered using ArraySegment?

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

Pelagian answered 14/7, 2009 at 1:51 Comment(10)
Fantastic! If this does what I think it does, this solves my problem exactly.Tarsia
It probably does what you want, but it doesn't support the default array syntax, nor does it support IEnumerable, so its not especially clean.Pelagian
This needs more upvote. In my own exp, ArraySegment copying is slightly faster too (after all I use arrays for speed critical stuffs)..Holm
@AlexBlack It looks like as of .NET 4.5, it implements IEnumerable<T> and a variety of other useful interfaces.Robbi
I 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!Schock
How would you use ArraySegment to answer the original question?Conflagration
@CraigMcQueen - Try the following single-line approach: IList<T> newArray = (IList<T>)new ArraySegment<T>(oldArray, beginIndex, endIndex);Candlefish
@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.Tanberg
This 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).Tanberg
ArraySegment does not support clear or remove element types methodsDerail
C
40

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.

Cumulus answered 7/7, 2009 at 14:28 Comment(2)
This is more elegant.Luker
This is exactly what I was looking for. This works for any IEnumerable. I can get an IEnumerable, IList, IArray, etc... with minimal fuss, inline if I need to. If I don't need the deep copy, I just remove the Select. Dropping Skip or Take allows me to control the range. Alternatively, I can mix it up with SkipWhile and/or TakeWhile.Tanberg
U
33

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();
Uranography answered 19/12, 2011 at 12:34 Comment(3)
Singe line and no need to add Linq. It's my preferred way.Slut
Still it doesn't clone the source... but it's a good approach anywayAntheridium
It 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/…Hildie
M
32

In C# 8, they've introduced a new Range and Index type, which can be used like this:

int[] a = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
Index i1 = 3;  // number 3 from beginning
Index i2 = ^4; // number 4 from end
var slice = a[i1..i2]; // { 3, 4, 5 }

References:

Mahlstick answered 25/1, 2019 at 16:41 Comment(1)
This should be the approved answer now.Bangweulu
A
13
string[] arr = { "Parrot" , "Snake" ,"Rabbit" , "Dog" , "cat" };

arr = arr.ToList().GetRange(0, arr.Length -1).ToArray();
Azikiwe answered 3/6, 2014 at 13:25 Comment(0)
S
8

You can do this fairly easially;

    object[] foo = new object[10];
    object[] bar = new object[7];   
    Array.Copy(foo, 3, bar, 0, 7);  
Slovenly answered 3/6, 2009 at 8:37 Comment(2)
No, bar will still be null. Array.Copy doesn't magically create a new array, especially since bar isn't passed with ref or out.Iconolatry
oh ya hey, your right, i did this in a hurry whops, but hey, maybe when your writing critique's you should put the correction, constructuive critisism is so much more usefull for everybody. so before that array.copy you do a "bar = new object[7];"Slovenly
C
8

Building on Marc's answer but adding the desired cloning behaviour

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

And if implementing ICloneable is too much like hard work a reflective one using Håvard Stranden’s Copyable library to do the heavy lifting required.

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

Note that the OX.Copyable implementation works with any of:

For the automated copy to work, though, one of the following statements must hold for instance:

  • Its type must have a parameterless constructor, or
  • It must be a Copyable, or
  • It must have an IInstanceProvider registered for its type.

So this should cover almost any situation you have. If you are cloning objects where the sub graph contains things like db connections or file/stream handles you obviously have issues but that it true for any generalized deep copy.

If you want to use some other deep copy approach instead this article lists several others so I would suggest not trying to write your own.

Choiseul answered 8/7, 2009 at 16:11 Comment(3)
The first is probably the desired solution, as he is asking for cloning. Note that with the Copy method, you probably don't even have to check for null, as it is an extension method, if the method itself already does that thing. Worth a try.Psephology
Yes I noted the null check but didn't want to confuse the OP in case he didn't read the source.Choiseul
Just a sidenote: The latest version of Copyable on GitHub does not require objects to have a parameterless constructor. :) See github.com/havard/copyableSuckerfish
J
5

I think that the code you are looking for is:

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

Jahdol answered 3/6, 2009 at 8:36 Comment(1)
I 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.Slovenly
I
3

As an alternative to copying the data you can make a wrapper that gives you access to a part of the original array as if it was a copy of the part of the array. The advantage is that you don't get another copy of the data in memory, and the drawback is a slight overhead when accessing the data.

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

}

Usage:

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
Inebriate answered 3/6, 2009 at 9:13 Comment(1)
@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.Inebriate
P
3

In C# 8.0, you can now do many fancier works including reverse indices and ranges like in Python, such as:

int[] list = {1, 2, 3, 4, 5, 6};
var list2 = list[2..5].Clone() as int[]; // 3, 4, 5
var list3 = list[..5].Clone() as int[];  // 1, 2, 3, 4, 5
var list4 = list[^4..^0].Clone() as int[];  // reverse index
Philippa answered 21/10, 2020 at 6:42 Comment(1)
.Clone() isn't necessary. The range indexer already creates a fresh array instance.Goldin
W
2

Array.ConstrainedCopy will work.

public static void ConstrainedCopy (
    Array sourceArray,
    int sourceIndex,
    Array destinationArray,
    int destinationIndex,
    int length
)
Wale answered 3/6, 2009 at 8:33 Comment(2)
That 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).Aesculapian
Thats 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.Wale
C
2

It does not meet your cloning requirement, but it seems simpler than many answers to do:

Array NewArray = new ArraySegment(oldArray,BeginIndex , int Count).ToArray();
Component answered 6/5, 2020 at 17:41 Comment(0)
W
1

There's no single method that will do what you want. You will need to make a clone method available for the class in your array. Then, if LINQ is an option:

Foo[] newArray = oldArray.Skip(3).Take(5).Select(item => item.Clone()).ToArray();

class Foo
{
    public Foo Clone()
    {
        return (Foo)MemberwiseClone();
    }
}
Wool answered 11/7, 2009 at 7:8 Comment(0)
T
1

How about useing 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);

Below is my original post. It will not work

You could use 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
Tanberg answered 13/7, 2009 at 20:15 Comment(0)
P
1

How about this:

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;

}

You then need to implement the ICloneable interface on all of the classes you need to use this on but that should do it.

Pairs answered 14/7, 2009 at 5:13 Comment(0)
A
1

I'm not sure how deep it really is, but:

MyArray.ToList<TSource>().GetRange(beginningIndex, endIndex).ToArray()

It's a bit of overhead, but it might cut out an unnecessary method.

Arce answered 31/1, 2010 at 22:23 Comment(0)
H
0

Cloning elements in an array is not something that can be done in a universal way. Do you want deep cloning or a simple copy of all members?

Let's go for the "best effort" approach: cloning objects using the ICloneable interface or binary serialization:

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

This is not a perfect solution, because there simply is none that will work for any type of object.

Herculie answered 12/7, 2009 at 9:3 Comment(2)
Shoudn't that be something like result[i-index] = (T)... ?Calycine
yes :) And not only that. The loop boundary is wrong. I'll fix it. Thanks!Herculie
G
0

As far as cloning goes, I don't think serialization calls your constructors. This may break class invariants if you're doing interesting things in the ctor's.

It seems the safer bet is virtual clone methods calling copy constructors.

protected MyDerivedClass(MyDerivedClass myClass) 
{
  ...
}

public override MyBaseClass Clone()
{
  return new MyDerivedClass(this);
}
Gander answered 12/7, 2009 at 22:35 Comment(2)
Whether 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.Aesculapian
This highlights another friction point of serialization: You have to provide default constructors.Gander
P
0

You can take class made by 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;
    }
}

and then

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));
        }
    }
Potman answered 8/7, 2015 at 15:24 Comment(0)
C
0

This is the optimal way, I found, to do this:

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

Hope It Helps!

Canea answered 9/5, 2017 at 8:1 Comment(0)
F
0

use extention method :

public static T[] Slice<T>(this T[] source, int start, int end)
    {
        // Handles negative ends.
        if (end < 0)
        {
            end = source.Length + end;
        }
        int len = end - start;

        // Return new array.
        T[] res = new T[len];
        for (int i = 0; i < len; i++)
        {
            res[i] = source[i + start];
        }
        return res;
    }

and you can use it

var NewArray = OldArray.Slice(3,7);
Ferrell answered 4/4, 2019 at 8:56 Comment(0)
B
0

Code from the System.Private.CoreLib.dll:

public static T[] GetSubArray<T>(T[] array, Range range)
{
    if (array == null)
    {
        ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
    }
    (int Offset, int Length) offsetAndLength = range.GetOffsetAndLength(array.Length);
    int item = offsetAndLength.Offset;
    int item2 = offsetAndLength.Length;
    if (default(T) != null || typeof(T[]) == array.GetType())
    {
        if (item2 == 0)
        {
            return Array.Empty<T>();
        }
        T[] array2 = new T[item2];
        Buffer.Memmove(ref Unsafe.As<byte, T>(ref array2.GetRawSzArrayData()), ref Unsafe.Add(ref Unsafe.As<byte, T>(ref array.GetRawSzArrayData()), item), (uint)item2);
        return array2;
    }
    T[] array3 = (T[])Array.CreateInstance(array.GetType().GetElementType(), item2);
    Array.Copy(array, item, array3, 0, item2);
    return array3;
}


Benedick answered 6/1, 2020 at 20:0 Comment(0)
S
0
array1 = [5,6,7,8];

int[] array2 = new int[2];

Array.ConstrainedCopy(array1, 1, array2, 0, 2);

array2 = [6,7];

Array.ConstrainedCopy takes five (5) parameters:

  1. source array (sourceArray)

  2. starting index of source array (sourceIndex)

  3. destination array (destinationArray)

  4. starting index of destination array (destinationIndex)

  5. number of elements to copy (length)

Simon answered 12/7, 2022 at 18:24 Comment(0)
H
-1
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();
        }
Hawkes answered 20/8, 2014 at 15:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.