4

I have an array, and I want to remove just one element, but without reordering keys. Is there an easy way without using delete or rebuilding the entire array?

Or alternatively clean up after delete to get rid of the undefined values, fixing the length again.

var array = ["valueone", "valuetwo"];

console.dir(array); // keys 0 and 1
array.splice(0, 1);
console.dir(array); // key 1 is now 0, do not want!
Nuoun
  • 53
  • 2
  • 5
  • 3
    What do you have against using delete? – j08691 Mar 05 '13 at 21:54
  • Not sure I understand what you mean by "remove" but couldn't you just use a statement like array[index]=undefined; or array[index]=null; ? – Steve Chambers Mar 05 '13 at 21:54
  • 1
    why do you want to do this? – AD7six Mar 05 '13 at 21:56
  • Basically I want to use the length of the array to clean up some other things, but I guess I need to set another variable for that. – Nuoun Mar 05 '13 at 21:58
  • 1
    @Nuoun that's not what he meant. He meant "why do you think this is the right way of achieving whatever it is you _really_ want to do?". Look up "XY problems" - you're fixated on using "Y" to solve "X", but instead of asking how to "solve X", you ask how to fix "Y". – Alnitak Mar 05 '13 at 21:59
  • Sorry, posted to soon. Why I want to do this is because of a time delayed piece of code that removes another object, the key value of the array is the id of the object. Before you raise an eyebrow it's for a polyphonic javascript synthesizer so time is an issue. – Nuoun Mar 05 '13 at 22:02
  • 1
    @Nuoun that still doesn't help. If you're thinking in terms of "keys" then arrays are _not_ the answer, objects are. Arrays have _indexes_, not _keys_. – Alnitak Mar 05 '13 at 22:03
  • @ Alnitak I'm beginning to arrive at that conclusion, I was just hoping there was an easier way but oh well. Thanks for the quick answers, appreciated! – Nuoun Mar 05 '13 at 22:05

3 Answers3

8

You can delete the elements of an array:

a = ['one', 'two'];
delete a[0];

// a is now [undefined, 'two'];

alternatively, set a[0] explicitly to undefined.

Note that an arrays .length parameter is automatically maintained by the system. If you intentionally set it to a higher number, you'll just get a whole load of undefined values for the missing keys:

a.length = 10;
// a is now [undefined, 'two', undefined x 8]

If these semantics are not acceptable to you, then you should consider using an Object instead. This will preserve your keys, and perhaps be more efficient, but you lose the .length property.

Alnitak
  • 334,560
  • 70
  • 407
  • 495
  • "Is there an easy way **without using delete** or rebuilding the entire array?" – j08691 Mar 05 '13 at 21:54
  • @j08691 yeah, I missed that bit. It's an artificial constraint, though. The element's index is still there, just with an `undefined` value. – Alnitak Mar 05 '13 at 21:56
  • Great answer, many thanks. Obviously I should move to an object instead and keep track of it's elements by other means. – Nuoun Mar 05 '13 at 22:15
2

couldn't you just explicitly set the value to undefined or null or an empty string. What are you trying to achieve?

var arr = ['aaaa','bbb','ccc','ddd'];
    arr[0]= undefined;
    //or
    arr[0]= null;
    ///or
    arr[0]= "";
    arr.length; <--- 4
AD7six
  • 63,116
  • 12
  • 91
  • 123
james emanon
  • 11,185
  • 11
  • 56
  • 97
  • I wanted to use the length of the array, which is not correct with undefined values. Also I'm a bit worried about it "filling up" with key values since they get added with every keystroke. But obviously an object is the way to go here. – Nuoun Mar 05 '13 at 22:07
  • you sure, I just tested and even when assigning an undefined, I still get a length of 4. – james emanon Mar 06 '13 at 07:52
1

Update 2018-09-07

This answer isn't very good, in my opinion. I provided an answer on How do I remove a property from a JavaScript Object that has received much more attention from me over the years and covers this case and goes into much more detail.

The point is, you should be using Array.prototype.splice and Array.prototype.slice.

array.splice(start, n) returns a subset of array from index start with n sequential elements, and removes this subset from the original array, creating a new array in the process.

let array = [1,2,3,4,5,6];
array.splice(2,3); // [3,4,5]
array; // [1,2,6]

array.slice(start, end) returns a subset of array from index start to index end without mutating the original. The behavior is a little different from splice, which is why I prefer to call it as array.slice(start, start + n).

let array = [1,2,3,4,5,6];
array.slice(2, 2 + 3); // [3,4,5]
array; // [1,2,3,4,5,6]

Of course you could set the index to a sentinel value like null or "", but if you are wanting the array to stay in the same order after a deletion, perhaps you should change your approach--why does "valuetwo" have to be at index 1? What useful information is even being held in this data structure if the contents are always the same as the keys needed to access them?


The original answer is below. And if I am going to keep the original text, perhaps I should elaborate on why it's bad advice.

You can use javascript's delete keyword.

delete array[index];

Don't do this. If your array is homogeneous (as it ought to be), then this will corrupt your array by introducing a second type (undefined). You should use array.splice() as discussed above, which will create a new array with the specified range omitted.

Unfortunately, this creates an undefined index inside of the array

var arr = ['pie', 'cake', 'fish'];
delete arr[1];
arr; // ['pie', undefined, 'fish']

Case in point.

You could also do this:

var arr = [9,8,7,6];
arr[1] = null;
arr;        // [9,null,7,6]
arr.length; // 4
var i = -1;
while(++i < arr.length){
    if(arr[i] && typeof(arr[i] === "number")){
        alert(arr[i]);
    }
}

You could, but you shouldn't. Not only is this unnecessary, and doesn't do anything useful (because all it's doing is calling alert), but it's actually broken.

if(arr[i] && typeof(arr[i] === "number")){
    alert(arr[i]);
}

You might expect this to only print our element if it is a non-zero number, but will in fact also run for values like "foo", [] and document.createElement("p"), because typeof(arr[i] === "number") will always return the value "boolean", which is a non-empty string, which is truthy and will therefore evaluate true. Which means the only requirement for alert to be called is that arr[i] is truthy. There are only six values in the entire language that will cause this if statement to not execute, and those are:

  • undefined
  • null
  • 0
  • "" (pronounced "empty string")
  • false
  • NaN

Or, if you don't NEED to use arrays, you could use an object and make everything easier:

var obj = {
    0: "first",
    1: "second",
    2: "third"
};
delete obj[1];
obj; // {0: "first", 2: "third"}
for(var i in obj){
    alert(obj[i]);
}

Which would instantaneously erase all of the advantages to using an array. Now you have a data set which may or may not be heterogeneous, which can't be filtered, mapped, reduced or transformed in any sane way, and you have to resort to things like for(i in obj) (which is extremely bug-prone if you dare to use a library like jQuery) to iterate over it. Luckily today we have fancy stuff like Object.keys(obj).map(k => obj[k]).forEach(function(el){ ... }), but that's no excuse to have bad data structures.

To get the length of an object:

getLength = function(obj){
    var i = 0, l = 0;
    for(i in obj){
        l++;
    }
    return l;
}
getLength(obj); // 3

Again, with arrays, this is unnecessary.

But remember that objects sort their indices by date of creation, not > by name. This shouldn't result in a road block, though.

To sort the indices of an object alphabetically:

sortObject = function (){
    var arr = [], i;
    for(i in this){
        arr.push({index:i,content:this[i]});
        delete this[i];
    }
    arr.sort();
    for(i in arr){
        var item = arr[i];
        this[item.index] = item.content;
    }
    return this; // make chainable
}
var obj = {
    acronym: "OOP",
    definition: "Object-Oriented Programming",
    article: "http://wikipedia.org/OOP"
};
sortObject.apply(obj); // indices are "acronym", "article", "definition"
array.sort(fn)

The whole point of an object is that its properties are unsorted, anyway. Sorting an unsorted list will hardly do anything useful.

Just to illustrate how much better arrays are at doing array things:

let array = ["pie", "cake", "fish", "brownie", "beef", ...];
/* do some stuff... */
array
.filter(el => exclude.indexOf(el) === -1)
.forEach(function(el){
    console.log(el);
});

if exclude is ["cake", "brownie"], then this will log the following to the console:

pie
fish
beef
...

Just try to imagine how many unnecessary lines of code it would take to do the same using the approach from the previous version of this answer.


Hope this helped

Hopefully this update helped.

Braden Best
  • 8,830
  • 3
  • 31
  • 43