Simplify code that "toggles" an array item - javascript

I use lodash to insert an item into an array if it's not there, and remove it if it exists, kind of "toggling".
My code looks like this:
var items = ['a', 'b', 'c'];
var itemToToggle = 'a';
if (_.includes(items, itemToToggle)) {
_.pull(items, itemToToggle)
}
else {
items.push(itemToToggle)
}
Which seems not perfect enough.
Can I simplify it to, ideally, have something like _.toggle(items, itemToToggle)?

Another way to do it would be to use lodash's xor
var items = ['a', 'b', 'c'];
var itemToToggle = 'a';
new_array = _.xor(items, [itemToToggle])
return new_array // ['b', 'c']
Which will add the item if it does not exist, and remove if it does.
It does this by comparing the two arrays (items and [itemToToggle]) and returning a new array that is a merge of the two arrays, minus duplicates.

Your code seems fine to me. The only thing, I can think of is using the length to see if an item was removed, and if not, add it:
function toggleValueInArr(arr, value) {
var originalLength = arr.length; // cache the original length
_.pull(arr, value).length === originalLength && arr.push(value); // check if the length is the same as the original - ie no item was not removed. If so, push it.
return arr;
}

Related

Splice dosen't work in forEach loop [duplicate]

I have this code which is supposed to iterate over each item in an array, removing items based on some condition:
//iterate over all items in an array
//if the item is "b", remove it.
var array = ["a", "b", "c"];
array.forEach(function(item) {
if(item === "b") {
array.splice(array.indexOf(item), 1);
}
console.log(item);
});
Desired output:
a
b
c
Actual output:
a
b
Obviously the native forEach method doesn't check after each iteration whether the item has been deleted, so if it is then the next item is skipped. Is there a better way of doing this, aside from overriding the forEach method or implementing my own class to use instead of an array?
Edit - further to my comment, I suppose the solution is to just use a standard for loop. Feel free to answer if you have a better way.
Lets see why JavaScript behaves like this. According to the ECMAScript standard specification for Array.prototype.forEach,
when you delete an element at index 1, the element at index 2 becomes the element at index 1 and index 2 doesn't exist for that object.
Now, JavaScript looks for element 2 in the object, which is not found, so it skips the function call.
That is why you see only a and b.
The actual way to do this, is to use Array.prototype.filter
var array = ["a", "b", "c"];
array = array.filter(function(currentChar) {
console.log(currentChar); // a, b, c on separate lines
return currentChar !== "b";
});
console.log(array); // [ 'a', 'c' ]
One possibility would be to use the array.slice(0) function, which creates a copy (clone) of the array and thus the iteration is separated from the deletion.
Then the only change to the original approach using array.forEach would be to change it to array.slice(0).forEach and it will work:
array.slice(0).forEach(function(item) {
if(item === "b") {
array.splice(array.indexOf(item), 1);
}
alert(item)
});
After the forEach, the array will contain only a and c.
A jsFiddle demo can be found here.
Using Array.prototype.filter as in thefourtheye's answer is a good way to go, but this could also be done with a while loop. E.g.:
const array = ["a", "b", "c"];
let i = 0;
while (i < array.length) {
const item = array[i];
if (item === "b") {
array.splice(i, 1);
} else {
i += 1;
}
console.log(item);
}
Another possibility would be to use the array.reduceRight function to avoid the skip:
//iterate over all items in an array from right to left
//if the item is "b", remove it.
const array = ["a", "b", "c"];
array.reduceRight((_, item, i) => {
if(item === "b") {
array.splice(i, 1);
}
}, null);
console.log(array);
After the reduceRight, the array will contain only a and c.

Remove element with custom index from array Javascript

I have a couple of arrays that looks a bit like these:
arr['a'] = 'val1';
arr['b'] = 'val2';
arr['c'] = 'val3';
The index is not an integer, it is a string. I want to remove arr['b'] from the array completely. I have tried:
arr.splice('b', 1);
It does not work, and it might be because the index in not an integer, according to
w3schools this is the problem "index - Required. An integer".
A possible solution could be looping through all arrays and re-creating them with an integer index and then an array holding the custom indexes as values and the equivalent integer index as its index.
This seems like a tad unnecessary and a waste of resources, is there a smarter more effective and simpler solution?
Preferably an arr.splice that will work with a non-integer index.
I have looked through plenty of posts that covers how to remove elements from arrays by index and values, but none covers how to remove elements using a non-integer index.
Example posts that I have found:
0
1
2
Any and all help is greatly appreciated!
//Edit, used following as a solution.
function aObj() {
this.a = "";
this.b = [];
}
var aObjs = [];
aObjs.push(new aObj);
aObjs.push(new aObj);
aObjs.push(new aObj);
aObjs[0].a = "val1";
aObjs.splice(1, 1);
Looks a bit different than what I used in my first example, but this is more accurate towards how I used it. May not be the best way to do it, but it works.
Don't use array for string indexes, use objects like bellow
var arr = {} //create a object
arr['a'] = 'val1'; //asign values
arr['b'] = 'val2';
arr['c'] = 'val3';
console.log(arr) //prints {a: "val1", b: "val2", c: "val3"}
delete arr['a'] //delete a key
console.log(arr) // prints {b: "val2", c: "val3"}
Well it does not work, because you are using an array as a dictionary, which it's not. First of all use object for that. Second use delete to remove a property:
var dict = { 'a': 'val1', 'b': 'val2', 'c': 'val3' };
delete dict.a;
As said before, this is not an Array. If it should be an array, it looks like this
var arr = ['val1', 'val2', 'val3'];
Now you can use Array.splice to remove value 'val2':
arr.splice(1,1);
// or
arr.splice(arr.indexOf('val2'),1);
// or even
arr = arr.filter(function (v){ return v !== 'val2'});
If it should be an object, its declariation looks like:
var obj = {a: 'val1', b: 'val2', c: 'val3'};
And if you want to delete 'val2' whilst not knowing the key for it you can loop:
for (var key in obj) {
if (obj[key] === 'val2';
delete obj[key];
}
// or (mis)use Object.keys
Object.keys(obj)
.filter(function(v){
return this[v] === 'val2' ? !(delete this[v]) : true;
}, obj);
Knowing this, you can create a helper method for Objects and Arrays:
function removeByValue(objOrArr, value) {
if (objOrArr instanceof Array && objOrArr.length) {
var found = objOrArr.indexOf(value);
if (found) { objOrArr.splice(found,1); }
}
if (objOrArr instanceof Object) {
var keys = Object.keys(objOrArr);
if (keys.length) {
keys.filter(function(v){
return this[v] === value ? !(delete this[v]) : true;
}, objOrArr);
}
}
return objOrArr;
}
// usage (using previous arr/obj)
removeByValue(arr, 'val2'); // arr now ['val1','val3']
removeByValue(obj, 'val2'); // obj now {a:'val1', c: 'val3'}
Example

count similarities between two arrays with javascript

Im stuck on a piece of javascript for the last 4 hours!
The question is how do I count similarities between 2 arrays like so:
arrayA = [a,b,c,d,e,f,g];
arrayB = [c,d,e];
The answer shoud be three. The only piece of code I have at the moment produces a infinite loop :(
Pleas help
One way would be to filter arrayA by checking each to see if it's in arrayB, then getting the length of the new array:
arrayA.filter(function(el) {
return arrayB.indexOf(el) >= 0;
}).length;
This uses:
Array#indexOf
Array#filter
Array#length
NB that the first two are not available in old browsers, so need to be shimmed using the code in the links given.
here you go (cross browser solution):
[note that .filter() method wont work in IE8 and other old browsers, so i suggest the following approach]
1) define the function:
function count_similarities(arrayA, arrayB) {
var matches = 0;
for (i=0;i<arrayA.length;i++) {
if (arrayB.indexOf(arrayA[i]) != -1)
matches++;
}
return matches;
}
2) call it:
var similarities = count_similarities(arrayA, arrayB);
alert(similarities + ' matches found');
if you don't bother about old browsers support, i'd highly suggest lonesomeday's answer.
hope that helps.
You should take each element of one array and check if it is present in the other using arrayA.indexOf(arrayB[i])
If it doesnt return -1 then increment a count variable.
At the end count is your answer.
You can go with $.inArray() function to do this
http://api.jquery.com/jQuery.inArray/
$(function () {
arrayA = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
arrayB = ['c', 'd', 'e'];
var matchCount = 0;
$.each(arrayB, function (index, value) {
if ($.inArray(value, arrayA) != -1)
matchCount++;
});
alert("Matched elements : " + matchCount);
});

In Javascript how to make arrays with custom behavior that can be instantiated?

I need an kind of "circular array". I have everything working but for single instance. I don't know how to make it "instantiable". I mean I want it to work the following way:
var arr = ['a', 'b', 'c', 'd']; // it's kind of pseudo-code
arr.getNext(); // gives a
arr.getNext(); // gives b
arr.getNext(); // gives c
arr.getNext(); // gives d
arr.getNext(); // gives a
arr.getNext(); // gives b
// and so on
I know I can create object with array inside and iterate over it, but I'm pretty sure I can do this the other way.
The problem is I need several instances of that object. If it was only one instance I could do:
var arr = ['a', 'b', 'c', 'd'];
arr.getNext = function() {
// ... I got this stuff working
}
How to allow createion of several instances of such custom arrays?
Even if you can extend Array.prototype with Object.defineProperty (to create a non-enumerable property), an alternative solution might be interesting as well, depending on your actual needs.
You could define a function that returns an iterator over an array:
function iter(arr) {
var index = -1;
return {
next: function() {
index = (index + 1) % arr.length;
return arr[index];
}
};
}
Usage:
var it = iter(arr);
it.next();
You can extend the prototype to be able to use it in all instances of Array:
Array.prototype.getNext = function(){
var next = this.shift();
this.push(next);
return next;
};
Note that this modifies the array.
You can add you getNext method to the Array prototype, like this:
Array.prototype.getNext = function ()
{
....
}
You can then call this method on any array that you create.

Javascript array search and remove string?

I have:
var array = new Array();
array.push("A");
array.push("B");
array.push("C");
I want to be able to do something like:
array.remove("B");
but there is no remove function. How do I accomplish this?
I'm actually updating this thread with a more recent 1-line solution:
let arr = ['A', 'B', 'C'];
arr = arr.filter(e => e !== 'B'); // will return ['A', 'C']
The idea is basically to filter the array by selecting all elements different to the element you want to remove.
Note: will remove all occurrences.
EDIT:
If you want to remove only the first occurence:
t = ['A', 'B', 'C', 'B'];
t.splice(t.indexOf('B'), 1); // will return ['B'] and t is now equal to ['A', 'C', 'B']
Loop through the list in reverse order, and use the .splice method.
var array = ['A', 'B', 'C']; // Test
var search_term = 'B';
for (var i=array.length-1; i>=0; i--) {
if (array[i] === search_term) {
array.splice(i, 1);
// break; //<-- Uncomment if only the first term has to be removed
}
}
The reverse order is important when all occurrences of the search term has to be removed. Otherwise, the counter will increase, and you will skip elements.
When only the first occurrence has to be removed, the following will also work:
var index = array.indexOf(search_term); // <-- Not supported in <IE9
if (index !== -1) {
array.splice(index, 1);
}
List of One Liners
Let's solve this problem for this array:
var array = ['A', 'B', 'C'];
1. Remove only the first:
Use If you are sure that the item exist
array.splice(array.indexOf('B'), 1);
2. Remove only the last:
Use If you are sure that the item exist
array.splice(array.lastIndexOf('B'), 1);
3. Remove all occurrences:
array = array.filter(v => v !== 'B');
DEMO
You need to find the location of what you're looking for with .indexOf() then remove it with .splice()
function remove(arr, what) {
var found = arr.indexOf(what);
while (found !== -1) {
arr.splice(found, 1);
found = arr.indexOf(what);
}
}
var array = new Array();
array.push("A");
array.push("B");
array.push("C");
​
remove(array, 'B');
alert(array)​​​​;
This will take care of all occurrences.
Simply
array.splice(array.indexOf(item), 1);
Simple solution (ES6)
If you don't have duplicate element
Array.prototype.remove = function(elem) {
var indexElement = this.findIndex(el => el === elem);
if (indexElement != -1)
this.splice(indexElement, 1);
return this;
};
Online demo (fiddle)
const changedArray = array.filter( function(value) {
return value !== 'B'
});
or you can use :
const changedArray = array.filter( (value) => value === 'B');
The changedArray will contain the without value 'B'
In case of wanting to remove array of strings from array of strings:
const names = ['1','2','3','4']
const excludeNames = ['2','3']
const filteredNames = names.filter((name) => !excludeNames.includes(name));
// ['1','4']
You have to write you own remove. You can loop over the array, grab the index of the item you want to remove, and use splice to remove it.
Alternatively, you can create a new array, loop over the current array, and if the current object doesn't match what you want to remove, put it in a new array.
use:
array.splice(2, 1);
This removes one item from the array, starting at index 2 (3rd item)
use array.splice
/*array.splice(index , howMany[, element1[, ...[, elementN]]])
array.splice(index) // SpiderMonkey/Firefox extension*/
array.splice(1,1)
Source:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice
This only valid on str list, look up this
myStrList.filter(item=> !["deletedValue","deletedValue2"].includes(item))
Here is the simplest answer.
First find index using indexofand then
if index exist use splice
const array = ['apple', 'banana', 'orange', 'pear'];
const index = array.indexOf('orange'); // Find the index of the element to remove
if (index !== -1) { // Make sure the element exists in the array
array.splice(index, 1); // Remove the element at the found index
}
console.log(array); // ["apple", "banana", "pear"]

Categories