DEV Community

Discussion on: JavaScript Interview Question #49: Add a new array element by index

Collapse
teamradhq profile image
teamradhq

I'd advise you consider assigning an element directly to an array index to be an anti-pattern and bad practice. As shown by your examples, this produces unexpected results and doesn't meet our requirements.

f we say add a **new array element* by index, if an array has length n then we should expect the result to be an array of length n + 1.

Both of these examples fail to meet that expectation:

let arr;

// n + 2 
arr = [ 1, 2, 3, 4]; // (4)
arr[5] = 5; // (6) [ 1, 2, 3, 4, <1 empty item>, 5 ]

// n + 0
arr = [ 1, 2, 3, 4]; // (4)
arr[3] = 5; // (4) [ 1, 2, 3, 5 ]
Enter fullscreen mode Exit fullscreen mode

Another issue is that might might overwrite an existing element:

const arr = [1, 2, 3];
arr[2] = 4; // [1, 2, 4] 
Enter fullscreen mode Exit fullscreen mode

I don't think overwriting elements or inserting empty ones is ever what we would want to do...

In my opinion, a better implementation for this interview question would be to write a function to splice a new element into array or add it to the end of the array:

function insertElementAtIndex(arr, index, element) {
  if (index < 1) {
    return [element, ...arr];
  }

  if (index >= arr.length) {
    return [...arr, element];
  }

    const output = [...arr];
  output.splice(index, 0, element);

  return output;
}
Enter fullscreen mode Exit fullscreen mode

Something like this would give us the expected result of an array with a newly inserted element:

insertElementAtIndex([1, 2], -5, 'insert'); // (3) ["insert", 1, 2]
insertElementAtIndex([1, 2],  0, 'insert'); // (3) ["insert", 1, 2]
insertElementAtIndex([1, 2],  1, 'insert'); // (3) [1, "insert", 2]
insertElementAtIndex([1, 2],  2, 'insert'); // (3) [1, 2, "insert"]
insertElementAtIndex([1, 2],  5, 'insert'); // (3) [1, 2, "insert"]
Enter fullscreen mode Exit fullscreen mode

For bonus points, you could allow insertion from end of array when a negative value is passed for index, and allow inserting multiple elements:

function insertelementsAtIndex(arr, position, ...elements) {
  const isOutOfRange = Math.abs(position) >= arr.length;
  const isNegative = position < 0;

  if (position === 0 || (isNegative && isOutOfRange)) {
    return [...elements, ...arr];
  }

  if (isOutOfRange) {
    return [...arr, ...elements];
  }

  const index = isNegative ? arr.length + position : position;
    const output = [...arr];
  output.splice(index, 0, ...elements);

  return output;
}
Enter fullscreen mode Exit fullscreen mode

If we pass a negative position to insertelementsAtIndex, we insert the element before the nth last element, and if it's out of range, we insert it at the start of the array... so -1 inserts before the last item, -2 before the second last item, etc:

insertelementsAtIndex([1, 2, 3], -5, 'insert'); // (4) ["insert", 1, 2, 3]
insertelementsAtIndex([1, 2, 3], -2, 'insert'); // (4) [1, "insert", 2, 3]
insertelementsAtIndex([1, 2, 3], -1, 'insert'); // (4) [1, 2, "insert", 3]
Enter fullscreen mode Exit fullscreen mode

And if we pass an array of length n, and x new elements to insertelementsAtIndex, we should expect an array of length n + x:

insertelementsAtIndex([1, 2, 3], -5, 'a', 'b'); // (5) ["a", "b", 1, 2, 3]
insertelementsAtIndex([1, 2, 3], -2, 'a', 'b'); // (5) [1, "a", "b", 2, 3]
insertelementsAtIndex([1, 2, 3], -1, 'a', 'b'); // (5) [1, 2, "a", "b", 3]
insertelementsAtIndex([1, 2, 3],  0, 'a', 'b'); // (5) ["a", "b", 1, 2, 3]
insertelementsAtIndex([1, 2, 3],  5, 'a', 'b'); // (5) [1, 2, 3, "a", "b"]
Enter fullscreen mode Exit fullscreen mode

If this was an interview question, I suspect it would be intended to verify that you avoid assigning to element index explicitly and instead provide some implementation similar to either of these two examples.

Collapse
coderslang profile image
Coderslang: Become a Software Engineer Author

Great comment! The original question is not about teaching beginners "how to", but rather about explaining the situation "what if".

And the best way to add a new element to the end of the JS array is by using a push method.

Collapse
knightkrusty profile image
Vaibhav • Edited
const arr = [1,2,3]
 const elements = "insert"
 const index = 2;
 const output = [...arr];
 console.log( output.splice(index, 0, ...elements))
Enter fullscreen mode Exit fullscreen mode

Hey i was just checking your code out, it works but why splice method for me returns an empty array but works for you.

Collapse
teamradhq profile image
teamradhq • Edited

It's because Array.splice deletes and inserts items in array, then returns any items that were deleted. So if we don't delete any items, return value of Array.splice(n, 0, ...items) will always be empty [].

In your example, you just need to log output as well as result of splice call:

const arr = [1,2,3]
const elements = "insert"
const index = 2;
const output = [...arr];

console.log( output.splice(index, 0, ...elements)) // []
console.log(output);
// (9) [1, 2, "i", "n", "s", "e", "r", "t", 3]
Enter fullscreen mode Exit fullscreen mode

And if you're wondering why you see individual characters from elements variable; it's because you're using rest parameter, which will spread iterable contents into function args. In case of string this is the individual characters of its value.

If this isn't what you want, and instead you just want to insert string value, you just need to remove rest operator:

const arr = [1,2,3]
const element = "insert"
const index = 2;
const output = [...arr];

output.splice(index, 0, element)
console.log(output);
// [1, 2, "insert", 3]
Enter fullscreen mode Exit fullscreen mode

This is also the reason why we spread arr into a new output array, before calling splice, because it mutates the array it's called on. When designing our implementation, it's better to avoid mutating input and let the consumer decide if they want to mutate.

For example, consumer "mutates" by assigning output of someFn to previously declared variable arr:

let arr = [1, 2, 3, 4];
arr = someFn(arr); 
console.log(arr);
['some', "mutation", 'has', 'occured'];
Enter fullscreen mode Exit fullscreen mode

I think the fact that splice is mutative leads a lot of people to avoid it. However, it's really useful! Here's some of the things you can do with it...

  • Remove items from array:
const arr = arr = [1, 2, 3, 4];
const result = arr.splice(1,2);
console.log(arr, result);
// (2) [1, 4] (2) [2, 3]
Enter fullscreen mode Exit fullscreen mode
  • Add items to array:
const arr = [1, 2, 3, 4];
const result = arr.splice(1, 0, 5, 6);
console.log(arr, result);
// (6) [1, 5, 6, 2, 3, 4] []
Enter fullscreen mode Exit fullscreen mode
  • Substitute items in array:
arr = [1, 2, 3, 4];
result = arr.splice(1, 2, 5, 6);
console.log(arr, result)
// (4) [1, 5, 6, 4] (2) [2, 3]
Enter fullscreen mode Exit fullscreen mode

So to summarise:

  • Array.splice mutates its array.
  • Array.splice returns items that were deleted from its array.
  • Array.splice syntax is:
Array.splice(start, deleteCount, ...items);
Enter fullscreen mode Exit fullscreen mode

Hope this helps explain it more ;)

Thread Thread
knightkrusty profile image
Vaibhav

Thank You for long explanantion, everything make sense now. I havent used these methods very often so forgot there nature and what it does and for some reason i havent noticed that it mutates the original array it self in mdn documentation. Stupid me...