I have read this question:
Deleting array elements in JavaScript - delete vs splice
And it appears that both splice and delete require an index of the element in order to remove, so how can I easily find the index when I have the value?
For example if I have an array that looks like this:
["test1", "test2", "test3"]
and I want to remove test2. The process I am using right now, which I'm hoping isn't the correct way to do it, is using $.each checking the value of each element in the array, maintaining a counter through the process (used as the index reference) and if the value is equal to "test2", then I have my index (in form of the counter) and then use splice to remove it.
While the array grows larger, I would imagine this would be a slow process, but what alternatives do I have?
You want to use the splice() function to remove the item, indexOf will find it in the array:
To Find a specific element in the Array: (to know which to remove)
var index = array.indexOf('test2');
Full Example:
var array = ['test1', 'test2', 'test3'];
var value_to_remove = 'test2';
array.splice(array.indexOf(value_to_remove), 1);
Working Demo
var array = ["test1", "test2", "test3"];
array.splice(array.indexOf("test2"), 1);
indexOf (source):
Returns the first index at which a given element can be found in the array, or -1 if it is not present.
You can jQuery's $.inArray and get rid of your $.each loop, and it works cross-browser (unlike Array.indexOf):
var index = $.inArray("test2", ["test1", "test2", "test3"]); // 1
(I realise your question is not tagged with 'jQuery', but you do mention that you're already using an $.each loop).
You find an element by value using Array#indexOf, and then use the resulting index. Note that older browsers may not have it, so you'll want to check to see if it's there and if not add it — here's some code from MDC that does the check and adds the function if it's not there.
if (!Array.prototype.indexOf) {
Array.prototype.indexOf = function (searchElement /*, fromIndex */ ) {
"use strict";
if (this === void 0 || this === null) {
throw new TypeError();
}
var t = Object(this);
var len = t.length >>> 0;
if (len === 0) {
return -1;
}
var n = 0;
if (arguments.length > 0) {
n = Number(arguments[1]);
if (n !== n) { // shortcut for verifying if it's NaN
n = 0;
} else if (n !== 0 && n !== (1 / 0) && n !== -(1 / 0)) {
n = (n > 0 || -1) * Math.floor(Math.abs(n));
}
}
if (n >= len) {
return -1;
}
var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0);
for (; k < len; k++) {
if (k in t && t[k] === searchElement) {
return k;
}
}
return -1;
}
}
Note that if you add things to the Array prototype like that, for..in loops that assume they'll only see array indexes (that is, incorrect but common for..in loops) will start having problems because they'll see the string "indexOf" when they're only expecting to see array indexes, see this blog post for details.
underscore.js is a really awesome little library with a lot of good utility functions. In this case #reject would be appropriate.
http://documentcloud.github.com/underscore/#reject
(Although the internal method is of course similar to your manual index lookup and slice/splice).
Related
I'm trying to implement a Linked List with Array's methods. Currently trying to implement indexOf and includes. I was looking into the following corner case:
var arr = [1,2,,4,5];
console.log(arr.indexOf(undefined));
console.log(arr.includes(undefined));
console.log(arr[2] === undefined);
Output:
-1
true
true
It looks like, for arr=[1,2,,4,5] it will actually keep undefined at arr[2]. Reading ECMA-262 and based on this page, indexOf uses "Strict Equality" (operator === ) and includes uses SameValueZero (Same as Object.is, except -0 is considered equal to +0). My functions are:
isStrictlyEqual(x,y) {
return x === y;
}
sameValue(x, y) {
return Object.is(x,y);
}
sameValueZero(x, y) {
return x === y || (Number.isNaN(x) && Number.isNaN(y));
}
The implementation of sameValueZero was taken from this topic.
My indexOf uses this isStrictlyEqual and my includes uses this sameValueZero. But, sameValueZero(undefined,undefined) returns true so My indexOf return index 2 for indexOf(undefined) while arr.indexOf(undefined) returns -1. Note that [1,2,undefined,4,5].indexOf(undefined) returns 2. So my only guess is that Array does not actually store this empty item as undefined.
I have a method that allows to fill a linked list from an existing array, like so:
_extend(iterable) {
for (var i = 0; i < iterable.length; ++i) {
this.push(iterable[i]);
}
In this case iterable[2] will return undefined so my DS will save undefined at index 2. Also tried to use forEach, but it actually skips the empty items.
I want my linked list to be similar to implementation of Array and follow ECMA specification. How my linked list should treat this corner case? Keeping undefined at that place, didn't work because of indexOf. I guess I need to distinguish between an empty item and undefined. How can I do so? I came across with this topic - does it mean I need to do two loops on the array? One regular for and one for in? Would I need to create a dummy node in that case? Does it make sense to do it for Linked list?
My linked list class:
class DoublyLinkedList {
constructor(array=null) {
this.head = null;
this.tail = null;
this.length = 0;
if (array !== null) {
this._extend(array);
}
}
}
Yes, you can (have to) add a dummy node and check for it in includes (and I guess also find and findIndex). Here's a POC:
const HOLE = Symbol()
class MyArray {
constructor() {
this.items = []
}
extend(a) {
for (let i = 0; i < a.length; i++)
this.items.push(
a.hasOwnProperty(i) ? a[i] : HOLE
)
}
indexOf(val) {
for (let i = 0; i < this.items.length; i++)
if (this.items[i] === val)
return i
return -1
}
includes(val) {
for (let i = 0; i < this.items.length; i++)
if (Object.is(this.items[i], val) || (val === undefined && this.items[i] === HOLE))
return true
return false
}
}
m = new MyArray()
m.extend([1, 2, 3, , 5])
console.log(m.indexOf(undefined))
console.log(m.includes(undefined))
m = new MyArray()
m.extend([1, 2, 3, undefined, 5])
console.log(m.indexOf(undefined))
console.log(m.includes(undefined))
This question already has answers here:
Check variable equality against a list of values
(16 answers)
Concise way to compare against multiple values [duplicate]
(8 answers)
Closed 7 years ago.
Whats the prettiest way to compare one value against multiples options?
I know there are loads of ways of doing this, but I'm looking for the neatest.
i ask because i'd hoped this was workable (it isn't, quite obviously when you look at it):
if (foobar == (foo||bar) ) {
//do something
}
Don't try to be too sneaky, especially when it needlessly affects performance.
If you really have a whole heap of comparisons to do, just format it nicely.
if (foobar === foo ||
foobar === bar ||
foobar === baz ||
foobar === pew) {
//do something
}
What i use to do, is put those multiple values in an array like
var options = [foo, bar];
and then, use indexOf()
if(options.indexOf(foobar) > -1){
//do something
}
for prettiness:
if([foo, bar].indexOf(foobar) +1){
//you can't get any more pretty than this :)
}
and for the older browsers:
( https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/IndexOf )
if (!Array.prototype.indexOf) {
Array.prototype.indexOf = function (searchElement /*, fromIndex */ ) {
"use strict";
if (this == null) {
throw new TypeError();
}
var t = Object(this);
var len = t.length >>> 0;
if (len === 0) {
return -1;
}
var n = 0;
if (arguments.length > 0) {
n = Number(arguments[1]);
if (n != n) { // shortcut for verifying if it's NaN
n = 0;
} else if (n != 0 && n != Infinity && n != -Infinity) {
n = (n > 0 || -1) * Math.floor(Math.abs(n));
}
}
if (n >= len) {
return -1;
}
var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0);
for (; k < len; k++) {
if (k in t && t[k] === searchElement) {
return k;
}
}
return -1;
}
}
Since nobody has added the obvious solution yet which works fine for two comparisons, I'll offer it:
if (foobar === foo || foobar === bar) {
//do something
}
And, if you have lots of values (perhaps hundreds or thousands), then I'd suggest making a Set as this makes very clean and simple comparison code and it's fast at runtime:
// pre-construct the Set
var tSet = new Set(["foo", "bar", "test1", "test2", "test3", ...]);
// test the Set at runtime
if (tSet.has(foobar)) {
// do something
}
For pre-ES6, you can get a Set polyfill of which there are many. One is described in this other answer.
You can use a switch:
switch (foobar) {
case foo:
case bar:
// do something
}
Just for kicks, since this Q&A does seem to be about syntax microanalysis, a tiny tiny modification of André Alçada Padez's suggestion(s):
(and of course accounting for the pre-IE9 shim/shiv/polyfill he's included)
if (~[foo, bar].indexOf(foobar)) {
// pretty
}
Why not using indexOf from array like bellow?
if ([foo, bar].indexOf(foobar) !== -1) {
// do something
}
Just plain Javascript, no frameworks or libraries but it will not work on IE < 9.
(foobar == foo || foobar == bar) otherwise if you are comparing expressions based only on a single integer, enumerated value, or String object you can use switch. See The switch Statement. You can also use the method suggested by André Alçada Padez. Ultimately what you select will need to depend on the details of what you are doing.
I like the pretty form of testing indexOf with an array, but be aware, this doesn't work in all browsers (because Array.prototype.indexOf is not present in old IExplorers).
However, there is a similar way by using jQuery with the $.inArray() function :
if ($.inArray(field, ['value1', 'value2', 'value3']) > -1) {
alert('value ' + field + ' is into the list');
}
It could be better, so you should not test if indexOf exists.
Be careful with the comparison (don't use == true/false), because $.inArray returns the index of matching position where the value has been found, and if the index is 0, it would be false when it really exist into the array.
This question already has answers here:
Check variable equality against a list of values
(16 answers)
Concise way to compare against multiple values [duplicate]
(8 answers)
Closed 7 years ago.
Whats the prettiest way to compare one value against multiples options?
I know there are loads of ways of doing this, but I'm looking for the neatest.
i ask because i'd hoped this was workable (it isn't, quite obviously when you look at it):
if (foobar == (foo||bar) ) {
//do something
}
Don't try to be too sneaky, especially when it needlessly affects performance.
If you really have a whole heap of comparisons to do, just format it nicely.
if (foobar === foo ||
foobar === bar ||
foobar === baz ||
foobar === pew) {
//do something
}
What i use to do, is put those multiple values in an array like
var options = [foo, bar];
and then, use indexOf()
if(options.indexOf(foobar) > -1){
//do something
}
for prettiness:
if([foo, bar].indexOf(foobar) +1){
//you can't get any more pretty than this :)
}
and for the older browsers:
( https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/IndexOf )
if (!Array.prototype.indexOf) {
Array.prototype.indexOf = function (searchElement /*, fromIndex */ ) {
"use strict";
if (this == null) {
throw new TypeError();
}
var t = Object(this);
var len = t.length >>> 0;
if (len === 0) {
return -1;
}
var n = 0;
if (arguments.length > 0) {
n = Number(arguments[1]);
if (n != n) { // shortcut for verifying if it's NaN
n = 0;
} else if (n != 0 && n != Infinity && n != -Infinity) {
n = (n > 0 || -1) * Math.floor(Math.abs(n));
}
}
if (n >= len) {
return -1;
}
var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0);
for (; k < len; k++) {
if (k in t && t[k] === searchElement) {
return k;
}
}
return -1;
}
}
Since nobody has added the obvious solution yet which works fine for two comparisons, I'll offer it:
if (foobar === foo || foobar === bar) {
//do something
}
And, if you have lots of values (perhaps hundreds or thousands), then I'd suggest making a Set as this makes very clean and simple comparison code and it's fast at runtime:
// pre-construct the Set
var tSet = new Set(["foo", "bar", "test1", "test2", "test3", ...]);
// test the Set at runtime
if (tSet.has(foobar)) {
// do something
}
For pre-ES6, you can get a Set polyfill of which there are many. One is described in this other answer.
You can use a switch:
switch (foobar) {
case foo:
case bar:
// do something
}
Just for kicks, since this Q&A does seem to be about syntax microanalysis, a tiny tiny modification of André Alçada Padez's suggestion(s):
(and of course accounting for the pre-IE9 shim/shiv/polyfill he's included)
if (~[foo, bar].indexOf(foobar)) {
// pretty
}
Why not using indexOf from array like bellow?
if ([foo, bar].indexOf(foobar) !== -1) {
// do something
}
Just plain Javascript, no frameworks or libraries but it will not work on IE < 9.
(foobar == foo || foobar == bar) otherwise if you are comparing expressions based only on a single integer, enumerated value, or String object you can use switch. See The switch Statement. You can also use the method suggested by André Alçada Padez. Ultimately what you select will need to depend on the details of what you are doing.
I like the pretty form of testing indexOf with an array, but be aware, this doesn't work in all browsers (because Array.prototype.indexOf is not present in old IExplorers).
However, there is a similar way by using jQuery with the $.inArray() function :
if ($.inArray(field, ['value1', 'value2', 'value3']) > -1) {
alert('value ' + field + ' is into the list');
}
It could be better, so you should not test if indexOf exists.
Be careful with the comparison (don't use == true/false), because $.inArray returns the index of matching position where the value has been found, and if the index is 0, it would be false when it really exist into the array.
I'm trying to use findIndex() on a object in google script but it does not work. Here is a exemple:
function myFunction() {
var searchingJohn = "John";
var DATA = {namesList: []};
DATA.namesList.push({name: "John", otherDataForThisName: []});
var result = DATA.namesList.findIndex(function (element)
{return element.name == this;}, searchingJohn);
return result;
}
this work well in a javascript consol but google script return me a "TypeError: Fonction findIndex not found in object....."
You can't.
You are trying to use findIndex() on a object, but findIndex() is for arrays.
From the MDN documentation on findIndex():
The findIndex() method returns the index of the first element in the array that satisfies the provided testing function. Otherwise -1 is returned.
The reason is that objects don't hold key-value pairs in any particular order. For example,
{key1:value1, key2:value2}
and
{key2:value2, key1:value1}
are exactly the same object. So there is no index to be found.
This is the example of using findIndex() function for searching string in object in google script. Example:
function myFunction() {
var DATA = {namesList: []};
DATA.namesList.push({name: "John", otherDataForThisName: []});
DATA.namesList.push({name: "Mark", otherDataForThisName: []});
DATA.namesList.push({name: "Twin", otherDataForThisName: []});
const searching = "Mark";
var result = DATA.namesList.findIndex(function (element){
return element.name.toLowerCase() == searching.toLowerCase();
});
return result;
}
findIndex() function is also use for 2 dimensional array.
var ss = SpreadsheetApp.getActiveSpreadsheet();
var cellRange = ss.getRange("B5:Q10").getValues();
var someString = "John";
if (cellRange == "John")
{
//Do this code.
}
This is the only way I can think to pull out the information you're trying to get. IndexOf(); pulls from an index ergo if "John" was in an array it would look like this ["J", "o", "h", "n"] which is why it can't be found that way.
The code above will find it in a range of cells and you can do the whole sheet but the more "Empty" you add to it the slower it will run. Another nested if loop could clean that up for you if you need a massive sheet checked.
I have the same issue with many basic javascript functionalities working with gscript, I added the polyfill at the top of my script and it worked with many functionalities.
Paste this code on the top of your script, it will support indexOf.
// This version tries to optimize by only checking for "in" when looking for undefined and
// skipping the definitely fruitless NaN search. Other parts are merely cosmetic conciseness.
// Whether it is actually faster remains to be seen.
if (!Array.prototype.indexOf)
Array.prototype.indexOf = (function(Object, max, min) {
"use strict"
return function indexOf(member, fromIndex) {
if (this === null || this === undefined)
throw TypeError("Array.prototype.indexOf called on null or undefined")
var that = Object(this), Len = that.length >>> 0, i = min(fromIndex | 0, Len)
if (i < 0) i = max(0, Len + i)
else if (i >= Len) return -1
if (member === void 0) { // undefined
for (; i !== Len; ++i) if (that[i] === void 0 && i in that) return i
} else if (member !== member) { // NaN
return -1 // Since NaN !== NaN, it will never be found. Fast-path it.
} else // all else
for (; i !== Len; ++i) if (that[i] === member) return i
return -1 // if the value was not found, then return -1
}
})(Object, Math.max, Math.min)
reference https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf#Polyfill
or do you just have to do a loop and check each element ?
Mozilla JS implementations and other modern JS engines have adopted an Array.prototype.indexOf method.
[1].indexOf(1) // 0
if it doesn't contain it, it returns -1.
IE of course and possibly other browsers do not have it, the official code for it:
if (!Array.prototype.indexOf)
{
Array.prototype.indexOf = function(elt /*, from*/)
{
var len = this.length >>> 0;
var from = Number(arguments[1]) || 0;
from = (from < 0)
? Math.ceil(from)
: Math.floor(from);
if (from < 0)
from += len;
for (; from < len; from++)
{
if (from in this &&
this[from] === elt)
return from;
}
return -1;
};
}
If you're using jQuery: jQuery.inArray( value, array )
Update: Pointed URL to new jQuery API
You can look at Javascript 1.6 for some functions.
https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Working_with_Arrays#Introduced_in_JavaScript_1.6
If you just want to know if it is in there, you could use indexOf for example, which would meet your needs.
UPDATE:
If you go to this page, http://www.hunlock.com/blogs/Mastering_Javascript_Arrays, you can find a function to use on IE and any other browser that doesn't have a built in function that you want to use.
Here's one way to have your own indexOf method. This version leverages the Array.prototype.indexOf method if it exists in the environment; otherwise, it uses its own implementation.
(This code has been tested, but I don't guarantee its correctness for all cases.)
// If Array.prototype.indexOf exists, then indexOf will contain a closure that simply
// calls Array.prototype.indexOf. Otherwise, indexOf will contain a closure that
// *implements* the indexOf function.
//
// The net result of using two different closures is that we only have to
// test for the existence of Array.prototype.indexOf once, when the script
// is loaded, instead of every time indexOf is called.
var indexOf =
(Array.prototype.indexOf ?
(function(array, searchElement, fromIndex) {
return array.indexOf(searchElement, fromIndex);
})
:
(function(array, searchElement, fromIndex)
{
fromIndex = Math.max(fromIndex || 0, 0);
var i = -1, len = array.length;
while (++i < len) {
if (array[i] === searchElement) {
return i;
}
}
return -1;
})
);