-2

How to add an element at middle of an array? I have tried searching on google but cannot find a way to do this without looping. Can anybody help me by providing a code snippet but please don't suggest using loop as the array is heavy and performance is the bottle neck here.

EDIT

actually i want that every odd index element gets copied to the even index.

Example

myarray[0] = "a";
myarray[1] = "b";
myarray[2] = "c";

Answer expected

myarray[0] = "a";
myarray[1] = "a";
myarray[2] = "b";
myarray[3] = "b";
myarray[4] = "c";
myarray[5] = "c";
13
  • Array is not the right data type for quick inserting. Commented Dec 19, 2014 at 12:16
  • but do you know a way to insert element in the middle? Commented Dec 19, 2014 at 12:17
  • stackoverflow.com/questions/10900079/… Commented Dec 19, 2014 at 12:18
  • List insert c# Commented Dec 19, 2014 at 12:18
  • 1
    No, there isn't. It's going to be at least linear in the number of items in the result. There's no way round that - unless you create a collection which actually only proxies requests for items to an original list. I strongly suspect your example isn't representative of your real needs though, which means it's still hard to help you. Commented Dec 19, 2014 at 12:32

4 Answers 4

8

How to add an element at middle of an array?

You can't add an element anywhere in an array. Once an array has been created, its size is fixed - you can modify elements, but you can't add or delete them.

You could use a List<T> instead, which does allow for insertion - but which will need to copy the elements that occur after the insertion point.

You could potentially use a LinkedList<T> instead, but then you'd need to loop in order to get to the right node.

You might want to consider using some kind of tree structure instead, which may make it easier to find the right spot and then insert the data.

Alternatively, consider a redesign - we don't have much context, but you might want to consider something like collecting all the data to start with, ignoring ordering, and then sorting it into the right order.

EDIT: For your example, I wouldn't use any sort of insertion - I'd create a new list from the old one, for example:

// Or use Enumerable.Repeat instead of creating an array for each element.
var newList = oldList.SelectMany(x => new[] { x, x }).ToList();

Or do it without LINQ:

var copies = 2; // Or whatever
var newList = new List<string>(oldList.Count * copies);
foreach (var item in oldList)
{
    for (int i = 0; i < copies; i++)
    {
        newList.Add(item);
    }
}

Fundamentally you're not going to make this any cheaper than O(N) where N is the number of items in the resulting list...

Sign up to request clarification or add additional context in comments.

5 Comments

please see my extension of the question
your answer with lambda is correct but cant use it as it blocks coding at debugging time, can you suggest another way than that
@newbee: I have no idea what you mean by "blocks coding at debugging time". You'll need to be a lot more specific than that. If you mean you can't use Edit and Continue, I'd suggest getting out of the habit of writing significant amounts of code during debugging. Prefer unit tests which are easy to rerun after making changes.
if you use a lambda expression on .net framework platform it blocks changes at debugging (which is my usual habbit)
@newbee: Then get out of that habit. It's not a productive one - this being just one example. If you change your code while you're debugging, how can you have any confidence that your changes wouldn't have affected the code that's already been run? Unit testing is a much better approach for almost all scenarios, IMO.
1

you can do so by using "Buffer.BlockCopy" but you have to create a seperate copy of array for it

1 Comment

please see my extension of the question
1

You can not add element in Array. But if you are using List then method looks like:

public void Operation(List<string> list)
{
    if (list == null || list.Count == 0) return;

    int count = list.Count;

    for (int i = 0; i <= count; i++)
        if (i % 2 == 0)
            list.Insert(i + 1, list[i]);
} 

7 Comments

without loop is it possible with list because my performance has gone down because of loop
@newbee You can optimize loot but it has to done with loop only. I don't find such linq extension which does this. Check with Zip extension that would help you to find it.
You've a false optimisation in pre-fetching list.Count, as it means e.g. {"1", "2", "3", "4", "5", "6"} will become {"1", "1", "2", "2", "3", "3", "4", "4", "5", "6"} with the last two not being doubled. You need to change i <= count to i != list.Count or i < list.Count.
@Jon Hanna Thanks for suggestion but my answer just a logic for question.. I think <= and != both condition are same.. In both condition 'i' wont get value greater than count of list
<= and != are not both the same, you can use < or != as you prefer here, both are fine so I suggested both. Your bug though is that you store count = list.Count but then list.Count changes so the memoised value is incorrect. You could have used count = list.Count * 2 as well to fix the bug while still memoising. Your efficiency flaw though is that you are copying count * (count - 1) / 2 elements over the course of running it, which makes it much slower than it needs to be.
|
1

To address the comment:

without loop is it possible with list because my performance has gone down because of loop

While it's not possible to avoid looping entirely and staying with the same structure (but see below), we can massively improve on the multiple inserts.

Every time you do an insert, all elements later in the list have to be copied within the internal structure. So for an n-element list there will be n-1 copy operations with a total of n * (n - 1) / 2 elements being copied (since the number of elements copied goes down each time. This means the time taken becomes roughly O(n2)—quadratic time. Which is bad.

We can do much better by first making the list double in size, and then copying the elements to their new positons. Since we only have to do n * 2 copies we are now doing this in O(n)—linear time.

To double the list's size we add it to itself. This is both convenient (we don't need to create another collection, or call Add() repeatedly) and takes advantage of the fact that List<T>.AddRange() has an optimisation for dealing well with lists being added to themselves:

list.AddRange(list);

Now, say the list contained {"1", "2", "3"}. It now contains {"1", "2", "3", "1", "2", "3"} and we want {"1", "1", "2", "2", "3", "3"}.

So we start with the last element of the original set (list[2]) and copy it to the last two positions, then move backwards through the list. Because we're moving backwards while copying forwards we don't end up copying into a position that has yet to be copied from (if we started in the beginning we'd just be copying the same "1" everywhere.

public static void DoupleUp(List<string> list)
{
  if(list == null || list.Count == 0)
      return;
  list.AddRange(list);
  for(int idx = list.Count / 2 - 1; idx != -1; --idx)
  {
    T el = list[idx];
    list[idx * 2] = el;
    list[idx * 2 + 1] = el;
  }
}

Now all that's left is to realise that there's nothing specifically string-related about this, and genericise the method so we can use it on all lists:

public static void DoupleUp<T>(List<T> list)
{
  if(list == null || list.Count == 0)
      return;
  list.AddRange(list);
  for(int idx = list.Count / 2 - 1; idx != -1; --idx)
  {
    T el = list[idx];
    list[idx * 2] = el;
    list[idx * 2 + 1] = el;
  }
}

After fixing the bug in Ankush's answer, I did a test run where I started with Enumerable.Range(0, 10).Select(i => i.ToString()).ToList() (the numbers 0 to 9 as strings) and then called his Operation on it 15 times, then went back to the original list and called my DoubleUp 15 times (15 isn't much, but it doubles each time so the final call is turning a list of 163840 elements into one with 327680 elements).

On my machine doing this with Operation takes around 12.8 seconds, while doing it with DoubleUp takes around 0.01 seconds.

If however you were going to iterate through this list only once, then you'd be much better creating the list on the fly:

public static IEnumerable<T> DoubleElements<T>(this IEnumerable<T> source)
{
  foreach(T item in source)
  {
    yield return item;
    yield return item;
  }  
}

Then you could just use foreach(string str in DoubleElements(myarray)) without even having to change from using an array.

You could even generalise this:

public static IEnumerable<T> RepeatElements<T>(this IEnumerable<T> source, int count)
{
  foreach(T item in source)
    for(int i = 0; i < count; ++i)
      yield return item;
}

And then use foreach(string str in RepeatElements(myarray, 2)).

Now, if you really need to avoid looping, you'll have to do something very different again, and it will take away some possible further use:

public class RepeatList<T> : IList<T>
{
  private readonly IList<T> _backing;
  private readonly int _repeats;
  public RepeatList(IList<T> backing, int repeats)
  {
    if(backing == null)
      throw new ArgumentNullException("backing");
    if(repeats < 1)
      throw new ArgumentOutOfRangeException("repeats");
    _backing = backing;
    _repeats = repeats;
  }
  public RepeatList(int repeats)
    : this(new List<T>(), repeats)
  {
  }
  public T this[int index]
  {
    get { return _backing[index / _repeats]; }
    set { _backing[index / _repeats] = value; }
  }
  public int Count
  {
    get { return _backing.Count * _repeats; }
  }
  public bool IsReadOnly
  {
    get { return _backing.IsReadOnly; }
  }
  public int IndexOf(T item)
  {
    int idx = _backing.IndexOf(item);
    return idx >= 0 ? idx * _repeats : -1;
  }
  public void Insert(int index, T item)
  {
    _backing.Insert(index / _repeats, item);
  }
  public void RemoveAt(int index)
  {
    _backing.RemoveAt(index / _repeats);
  }
  public void Add(T item)
  {
    _backing.Add(item);
  }
  public void Clear()
  {
    _backing.Clear();
  }
  public bool Contains(T item)
  {
    return _backing.Contains(item);
  }
  public void CopyTo(T[] array, int arrayIndex)
  {
    if(array == null)
      throw new ArgumentNullException("array");
    if(arrayIndex < 0)
      throw new ArgumentOutOfRangeException("arrayIndex");
    if(array.Length - arrayIndex < _backing.Count * _repeats)
      throw new ArgumentException("array is too small to copy all elements starting from index " + arrayIndex);
    foreach(T item in this)
      array[arrayIndex++] = item;
  }
  public bool Remove(T item)
  {
    return _backing.Remove(item);
  }
  public IEnumerator<T> GetEnumerator()
  {
    foreach(T item in _backing)
      for(int i = 0; i != _repeats; ++i)
        yield return item;
  }
  IEnumerator IEnumerable.GetEnumerator()
  {
    return GetEnumerator();
  }
}

This will create such a list in constant time: var rep = new RepeatList<string>(myArray, 2) returns pretty much immediately.

It mutates as myArray does (change element 2 of myArray and you change elements 4 and 5 of rep) and if used with a non-readonly backing list this goes both ways, including calls to Add in fact adding 2 elements. You can minimise the strangeness by making it readonly and having all of the mutating members throw NotSupportedException but it can also be useful to have the write-through happen.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.