Thought I would share this in case people needed it as I couldn't find something similar.
I am wondering if it is possible to remove an item from an array even if duplicates of that item exist.
Lets look at some code:
var myArray = ['a', 'a', 'a', 'b', 'b', 'b', 'c', 'd', 'd', 'e'],
itemToRemove = 'a',
itemToAdd = 'f';
For the example above, I want to remove itemToRemove from the array, BUT, I do not want to remove all of them, just 1. I then want to add itemToAdd.
To give a little context, I am build a category/personality based quiz, each answer to a question has a category. I want to store all chosen categories in an array, then display a result dependent on which category is most common at the end of the quiz.
Where the question comes in is the User can go back and change their choice if they want, so if they do change their mind, I need to remove the previous category and add the new one.
So this answer to this is actually very simple.
var index = myArray.indexOf(itemToRemove);
if(index != -1) {
array.splice(index, 1);
}
As the indexOf function only finds the first index and only finds 1 index this can be used to remove just 1 of the array items.
We also need to be careful with indexOf as it is not supported with IE8, as always IE8 is a pain in the ass.
So the following code can make indexOf work for you, it must be run before you use the function.
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;
};
}
Sourced from here:
Why doesn't indexOf work on an array IE8?
If anyone has any ideas of a better way to do this they would be much appreciated.
Related
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;
}
Essentially I want to port the solution for: Python spliting a list based on a delimiter word to JavaScript.
Given: var example = ['A', 'WORD', 'B' , 'C' , 'WORD' , 'D'];
If a delimiter of WORD is provided I would like to generate:
var result = [['A'], ['WORD','B','C'],['WORD','D']];
Is there an alternative to looping through the list to do this?
The best approach here is to first write down the algorithm, without getting into specific code. Sometimes this is called pseudo-code. Have you tried writing some? Here's an example:
Start off with an empty result of the form [[]]. The inner array we will call subarray.
Look at the next word in the input. If it's 'WORD', then add a new subarray to the result and make it the current subarray.
Add the word to the current subarray.
Repeat until input is empty.
This type of algorithm, where we are looping over an array, and building up some kind of result, is exactly what reduce was designed for. We can transform the pseudo-code above almost directly into JS as follows:
function split(array) {
var subarray = []; // subarray we are adding elts to
return array.reduce( // loop over the array
function(result, elt) { // and for each element
if (elt === 'WORD') // if it is word, then...
result.push(subarray = []); // start a new subarray and add to result
subarray.push(elt); // add the current element to the subarray
return result; // and return the updated result
},
[subarray]); // start off with an array with a single subarray
}
Using generators
If you are working in an ES6 environment, you could use ES6 generators:
function* group(array) {
var subarray = [];
for (var elt of array) {
if (elt === 'WORD') {
yield subarray;
subarray = [];
}
subarray.push(elt);
}
yield subarray;
}
Here, array can actually be any iterable, since we are using for..of to get its values.
Now you can print out your subarrays by
for (grp of group(example)) console.log(grp);
Or create an array of the groups:
Array.from(group(examples))
Is there an alternative to looping through the list to do this?
Someone is going to have to loop, be it you or some library routine. In the first case, it is reduce doing the looping; in the ES6 code, it is the generator.
this should do what you ask
var tree = function (seq, breaks) {
var res = [], n;
for(var i = 0; i < seq.length; ) {
if(breaks.indexOf(seq[i]) != -1) {
for(var j = i+1; j < seq.length; j++) {
if(breaks.indexOf(seq[j]) != -1) {
break;
}
}
n = j;
var branch = tree(seq.slice(i+1, n), breaks);
branch.unshift(seq[i]);
res.push(branch);
i+=branch.length;
} else {
res.push([seq[i]]);
i++;
}
}
return res;
}
use
tree(['A', 'WORD', 'B' , 'C' , 'WORD' , 'D'], ['WORD'])
So if I had:
A=['a','b','c'];
B=[];
for (var i = 0;i<7;i++){
B.push(A[i])
}
I would get
B=["a", "b", "c", undefined, undefined, undefined, undefined]
Instead I want
B= ["a","b","c"]
So I guess I would need something like
for (var i = 0;i<7;i++){
B.push(A[i] "unless A[i] is undefined. Then don't push anything")
}
How would I do this in Javascript?
Some ES magic
[1, 2, undefined].filter(Boolean)
will produce
[1, 2]
I think this is what you're searching:
Array#filter
B = A.filter(function (element) {
return element !== undefined;
});
That Code will make a duplicate of A filtering out all undefined elements.
Instead of iterating the array and pushing elements one at a time to a new array, it's usually more efficient to use the Array.filter function, like so:
var B = A.filter(function(item){ return item !== undefined; });
You may also want to be cautious about using undefined. This is easily confused with other constructs such as null or empty strings. If you use "!=" instead of "!==" in the function, that will filter out null or missing array items, as well as items which are set specifically as undefined.
You could add a safeguard like
A[i] && B.push(A[i])
which would only push if A1 returns a true, but that would also give false if A[i] would be 0
Example
I would suggest checking against null
if (A[i] != null) B.push(A[i])
Example
If you want to keep your first forloop as it is you can "clean" the array after by using filter. Like:
for (var i = 0; i < 7; i++) {
B.push(A[i])
}
var clean_B = B.filter(function (item) {
return item != null;
});
Example
An Object Oriented Aspect
=> After that code, clean your array :
B.clean();
Known that clean is an extension of array Class(OOP context)
Array.prototype.clean=function(){
var newArray = [];
for(var index = 0; index < this.length; index++) {
if(this[index]) {
newArray.push(this[index]);
}
}
return newArray;
};
You stated the answer to your question in your sort-of comment (use // at the end of the line next time, no need for inserting a comment in a string).
unless A[i] is undefined. Then don't push anything
Another way of putting it is, if A[i] is not undefined, then push.
When put like that, it directly translates into code.
if (A[i] !== undefined)
{
B.push(A[i]);
}
I elaborated on your comment to show you how easily you can figure these types of questions out with a little effort and time.
However, as others have stated, a much better way of ignoring undefined indices is to simply iterate to A.length, however this only works if there are no undefined values in-between the defined values (which is still likely the case).
Your problem would be solved by iterating from 1 to array.length, or by using a for in loop
If you're going to be dealing with arrays that could have undefined elements, for example [0, undefined, 4], then you'd need to check if the element is defined before you add it. if(a[i] !== undefined) will do the trick.
You would use an if statement
for (var i = 0; i < 7; i++){
if (A[i] !== undefined) {
B.push(A[i]);
}
}
You can have a compact function to filter out undefined values:
function compact(col) {
return col.filter(function(val) {
return val !== undefined;
});
}
console.log(compact([false,0,1,null,undefined])); // [false,0,1,null]
JS Bin Example
http://jsbin.com/mabowana/4/edit
initials:
A = ['a', 'b', 'c'];
B = [];
I think, the closest answer to OP's question would be this:
for (var i = 0; i < 7; i++) {
if (typeof A[i] !== "undefined") B.push(A[i]);
else continue;
}
or this one:
for (elem of A) if (typeof elem !== "undefined") B.push(elem); else continue;
Instead of hard coding i < 7, use i < A.length
for (var i = 0; i < A.length; i++){
B.push(A[i])
}
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);
});
Simple question, but i dont know how to solve it
I have several arrays, but i only want the values that all arrays have in common
Im using javascript.
Try looking for the value in each of the arrays using indexOF.
I never knew IE didn't support indexOf, but here's a quick fix from this post.
Something like this should work:
function getCommonElements() {
var common = [],
i, j;
if (arguments.length === 0)
return common;
outerLoop:
for (i = 0; i < arguments[0].length; i++) {
for (j = 1; j < arguments.length; j++)
if (-1 === arguments[j].indexOf(arguments[0][i]))
continue outerLoop;
common.push(arguments[0][i]);
}
return common;
}
Call it with any number of arrays as arguments:
var commonEls = getCommonElements(arr1, arr2, arr3, etc);
In case it's not obvious, the idea is to loop through the array from the first argument and test each of its elements against the other arrays. As soon as a particular element is found to not be in any of the other arrays from the other arguments continue on with the next element. Otherwise add the current element to the output array, common.
If you need to support browsers (IE < 9) that don't support the Array.indexOf() method you can either include the shim shown at the MDN page or replace the .indexOf() test from my code with another loop.
I think this should work.
var arr1 = [1,2,3,4]
, arr2 = [2,3,4,5]
, arr3 = [3,4,5,6]
, arrs = [arr1, arr2, arr3];
var all = arr1.concat(arr2.concat(arr3)).sort()
, red1 = all.filter(
function(val, i, arr) {
return i === arr.lastIndexOf(val)-1;
})
, red2 = red1.filter(
function(val, i, arr) {
var shared = true;
arrs.forEach(
function(arr, i, src) {
if (arr.indexOf(val) === -1)
shared = false;
})
return shared;
})
If you are only concerned with modern browsers that support reduce(), then use this solution:
Finding matches between multiple JavaScript Arrays
If you must support IE6, then use my solution below. Here's how I got this to work in IE6 using jQuery:
// Find common values across all arrays in 'a',
// where 'a' is an array of arrays [[arr1], [arr2], ...]
Object.common = function(a) {
var aCommon = [];
for (var i=0,imax=a[0].length,nMatch,sVal; i<imax; i++) {
nMatch = 0;
sVal = a[0][i];
for (var j=1,jmax=a.length; j<jmax; j++) {
nMatch += ($.inArray(sVal, a[j])>-1) ? 1 : 0;
}
if (nMatch===a.length-1) aCommon.push(sVal);
}
return aCommon;
}
Basically, you just loop through each value of the first array in 'a' to see if it exists in the other arrays. If it exists, you increment nMatch, and after scanning the other arrays you add the value to the aCommon array if nMatch equals the total number of the other arrays.
Using the sample data provided by Florian Salihovic, Object.common(arrs) would return [3, 4].
If you cannot use jQuery, then replace $.inArray() with the code provided by Mozilla:
https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/IndexOf