So, I'm trying to match 2 different arrays. If the same cells match, I want to remove that cell from one array using the .slice method.
Edit: What I'm trying to do is remove a number from array1 if array2 contains a matching number. The way the code works now is that it only deletes 1 entry. I want all the entries deleted from the first array.
array1 = [1, 2, 4, 5, 7, 10];
array2 = [1,2,4,5,6,7,8];
var misc = function deleteValues(array, arayy) {
for(var i = 0; i < array.length; i++) {
if ( arayy[i] == array[i]) {
array.splice(i, 1);
}
}
return array;
};
I try to run this and under console log, the array1 is unchanged. It seems like the splice method isn't removing any cells. I searched SE, but couldn't find anything that could help me.
jsFiddle Demo
The problem is that you are modifying one of the arrays as you iterate, but you are still using the same index. The end result is that you end up comparing the wrong indexes to each other after the first removal. Use two indexes, have one offset back down when it removes an item, and have the other simply iterate.
var misc = function deleteValues(array, arayy) {
for(var i = 0, j = 0; i < array.length; i++, j++) {
if ( arayy[j] == array[i]) {
array.splice(i--, 1);
}
}
return array;
};
It seems you want to remove items from the first array if the values are also in the second. The reduceRight method seems suitable as it iterates from right to left over the array, hence removing items doesn't affect the index of subsequent elements in the array. The same result can be achieved with a decrementing loop.
Also, I think function declarations are better than assignment of expressions, but each to their own.
var array1 = [1, 2, 4, 5, 7, 10];
var array2 = [1,2,4,5,6,7,8];
function deleteValues(arr0, arr1) {
arr0.reduceRight(function(x, value, index) {
if (arr1.indexOf(value) != -1) {
arr0.splice(index, 1);
}
},'');
// Not necessary but handy for chaining
return arr0;
}
document.write(deleteValues(array1, array2));
Using an arrow function, the above can be reduced to:
function deleteValues(arr0, arr1) {
arr0.reduceRight((x, v, i) => arr1.indexOf(v) != -1? arr0.splice(i, 1):'');
return arr0;
}
Related
I'm trying to remove an item from an array using the indexOf() with splice() technique suggested. This is what's happening
let someArray: string[] = [first, second, third, fourth, fifth, sixth];
let newArray: string[] = someArray.splice(3, 1);
console.log(newArray);
//deisred result = [first, second, third, fifth, sixth]
//result I'm getting = [fourth]
That's not what virtually every article I've come across says should happen. Can someone shed light on this?
UPDATE
I discovered this problem in my code when I was only ghetting 1 result where I was expecting more and tracked it back to this point.
Because when you splice an array you are mutating it, which means you are changing the original array. You're storing the result (the element you're splicing from the array) within the "newArray" variable that you have created here. So this:
var arr = [1, 2, 3, 4];
var mine = arr.splice(1, 1);
console.log(mine);
console.log(arr);
would return the original ray minus index one if we print arr to the console, and will return [2] if we print mine to the console. To get the output you're expecting, you would have to perform a different operation such as iterating through the array and utilizing splice differently. Here is an example:
var arr = [1, 2, 3, 4];
var mine = [];
for(var i = 0; i < arr.length; i++) {
if(i !== 3) {
mine.push(arr[i]);
}
}
Now I am not mutating the original array, and I am simply pushing the elements to a new array.
But if you want to simply mutate the original array and not store the new array in some sort of variable you can simply splice the original array:
var arr = [1, 2, 3, 4];
arr.splice(3, 1);
console.log(arr);
However, if you are passing it to a function, i'd probably not mutate an array outside of the function, and i'd simply return a value and store that value in a new variable:
var arr = [1, 2, 3, 4];
function deleteIndex(ar, i) {
var a = [];
ar.forEach(function(elt, index) {
if(index === i) {
}
else {
a.push(elt);
}
});
return a;
}
var newArr = deleteIndex(arr, 3);
console.log(newArr);
This way you can delete any index, or pass a function and criteria that you would want to use to determine if an index should be deleted, without changing to top-level structure of your original array by utilizing functional programming. There are also some function in the underscore module that can help you if that's the case.
I have an array of numbers with a bunch of duplicates. I need to get rid of them so I put the code:
let dup = arr.filter((elem, pos)=> arr.indexOf(elem) !== pos);
// dup Array contains the duplicate numbers
arr = arr.filter((elem, pos)=> arr.indexOf(elem) == pos);
//arr contains the whole array with duplicates
let i = 0;
let j = 0;
while(i<arr.length){
while(j<dup.length){
if(arr[i] == dup[j]){
arr.splice(i, 1);
//the splice method resets the decrease the index of the array so
i--;
};
j++;
};
i++
}
The problem is that the if doesn't run after the first match.So the array splice the first duplicate that it finds and stops. How can I fix that?
From Get all unique values in a JavaScript array (remove duplicates)
const myArray = ['a', 1, 'a', 2, '1'];
const unique = [...new Set(myArray)];
// output ["a", 1, 2, "1"]
or as a function
const unique = [...new Set(myArray)]
The problem is that you never reset j. You need to move that inside the while (i ...) loop.
let arr = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5]
console.log('arr', arr)
let dup = arr.filter((elem, pos)=> arr.indexOf(elem) !== pos);
console.log('dup', dup)
arr = arr.filter((elem, pos)=> arr.indexOf(elem) == pos)
console.log('new arr', arr)
let i = 0;
while(i<arr.length){
let j = 0;
while(j<dup.length){
if(arr[i] == dup[j]){
arr.splice(i, 1);
i--;
};
j++;
};
i++
}
console.log('final arr', arr)
But there are easier ways to do this.
Update
I got pinged for mentioning easier ways without showing one. Here is an alternative way to get the same results:
const singletons = (
xs,
dups = xs .filter ((x, i) => arr .indexOf (x) !== i)
) => xs .filter (x => dups .indexOf (x) < 0)
let arr = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5]
console .log (
singletons (arr)
)
This version does not modify your original array, just returning a new one containing only the singleton elements (those that appear just once in the original list.)
Explanation
singletons is a function taking an array of some type of element and returning another array of the same type. Because that type is not specific, I use a non-specific name; a fairly strong convention make this xs. with the s noting that it's plural (i.e. an array of them.)
dups is an array of elements that is duplicated in your original. Although I include it as a defaulted parameter, we could just as easily have created it in the body of the function like this:
const singletons = (xs) => {
const dups = xs .filter ((x, i) => arr .indexOf (x) !== i)
return xs .filter (x => dups .indexOf (x) < 0)
}
The only reason I didn't was that I am perhaps over-fond of single expression bodies, thus avoiding the {-} pair and the return statement. But there is no real difference between these two approaches, except that the one I presented happens to do some additional work that I would never count on: if you supply a second parameter, an array of values, then, rather than removing the duplicates, it removes all those element from your array that are also in the second one, vaguely reminiscent of a set difference function.
The main advantage of this over your approach is that it is non-destructive. It does not mutate your original data. It also has no assignments, except in the default parameter, so there is no confusion in state management. ("Where do I put let j == 0?" is not a meaningful question here.) This makes it feel more robust.
You need to set j=0 inside the first while loop, otherwise, it will only run through the second while loop once.
Also, if I were you, I would use a for-loop (array.forEach() specifically) instead of while, because they already count the number of elements anyway.
My solution for this is this:
arr.forEach((e, i) => {
dup.forEach((f, j) => {
if(e==f){
arr.splice(i, 1);
}
})
});
Hope it works for you.
EDIT:
Stolen from the comment from James to the original question.
Javascript already provides an easy method to do just that anyway:
arr.filter(x=> dup.indexOf(x) < 0)
The task is:
You will be provided with an initial array (the first argument in the
destroyer function), followed by one or more arguments. Remove all
elements from the initial array that are of the same value as these
arguments.
While working through it I found some Array.filter behaviour I'm struggling to understand:
function destroyer(arr) {
for (var i = 1; i<arguments.length; i++){
toDelete = arguments[i];
arr.filter(isItIn);
}
return arr;
}
function isItIn(item, undefined, array){
return item!=toDelete;
}
destroyer([1, 2, 3, 1, 2, 3], 2, 3);
My intent here was to iterate through items 1+ of the arguments, calling arr.filter each time. Arr.filter then calls isItIn which checks if the currently examined item is the one I'm searching for. However, arr is being returned unchanged. When I change isItIn to:
function isItIn(item, undefined, array){
return item==1;
}
to test, it is still unchanged, however console.logs in the original writing of isItIn show that it is receiving the arguments correctly (or so far as I've thought to determine at least.
Please note, I've solved the problem through another route, I'm not looking for a solution to the problem, merely an explanation of where my initial code went wrong.
Basically you use Array#filter and omit the result of it.
You need to assign the result of filter to the former array.
arr = arr.filter(isItIn);
function destroyer(arr) {
for (var i = 1; i < arguments.length; i++) {
toDelete = arguments[i];
arr = arr.filter(isItIn);
}
return arr;
}
function isItIn(item) {
return item != toDelete;
}
console.log(destroyer([1, 2, 3, 1, 2, 3], 2, 3));
You have to assign the returned array:
function destroyer(arr) {
for (var i = 1; i<arguments.length; i++) {
toDelete = arguments[i];
arr = arr.filter(isItIn);
}
return arr;
}
I'm trying to solve a freeCodeCamp exercise with this goal:
Write a function that takes two or more arrays and returns a new array
of unique values in the order of the original provided arrays.
In other words, all values present from all arrays should be included
in their original order, but with no duplicates in the final array.
The unique numbers should be sorted by their original order, but the
final array should not be sorted in numerical order.
So what I do is concatenate all the arguments into a single array called everything. I then search the array for duplicates, then search the arguments for these duplicates and .splice() them out.
So far everything works as expected, but the last number of the last argument does not get removed and I can't really figure out why.
Can anybody please point out what I'm doing wrong? Please keep in mind that I'm trying to learn, so obvious things probably won't be obvious to me and need to be pointed out. Thanks in advance.
function unite(arr1, arr2, arr3) {
var everything = [];
//concat all arrays except the first one
for(var x = 0; x < arguments.length; x++) {
for(var y = 0; y < arguments[x].length; y++) {
everything.push(arguments[x][y]);
}
}
//function that returns duplicates
function returnUnique(arr) {
return arr.reduce(function(dupes, val, i) {
if (arr.indexOf(val) !== i && dupes.indexOf(val) === -1) {
dupes.push(val);
}
return dupes;
}, []);
}
//return duplicates
var dupes = returnUnique(everything);
//remove duplicates from all arguments except the first one
for(var n = 1; n < arguments.length; n++) {
for(var m = 0; m < dupes.length; m++) {
if(arguments[n].hasOwnProperty(dupes[m])) {
arguments[n].splice(arguments[n].indexOf(dupes[m]), 1);
}
}
}
//return concatenation of the reduced arguments
return arr1.concat(arr2).concat(arr3);
}
//this returns [1, 3, 2, 5, 4, 2]
unite([1, 3, 2], [5, 2, 1, 4], [2, 1]);
Looks like you overcomplicated it a bit ;)
function unite() {
return [].concat.apply([], arguments).filter(function(elem, index, self) {
return self.indexOf(elem) === index;
});
}
res = unite([1, 2, 3], [5, 2, 1, 4], [2, 1], [6, 7, 8]);
document.write('<pre>'+JSON.stringify(res));
Explanations
We split the problem into two steps:
combine arguments into one big array
remove non-unique elements from this big array
This part handles the first step:
[].concat.apply([], arguments)
The built-in method someArray.concat(array1, array2 etc) appends given arrays to the target. For example,
[1,2,3].concat([4,5],[6],[7,8]) == [1,2,3,4,5,6,7,8]
If our function had fixed arguments, we could call concat directly:
function unite(array1, array2, array3) {
var combined = [].concat(array1, array2, array3);
// or
var combined = array1.concat(array2, array3);
but as we don't know how many args we're going to receive, we have to use apply.
someFunction.apply(thisObject, [arg1, arg2, etc])
is the same as
thisObject.someFunction(arg1, arg2, etc)
so the above line
var combined = [].concat(array1, array2, array3);
can be written as
var combined = concat.apply([], [array1, array2, array3]);
or simply
var combined = concat.apply([], arguments);
where arguments is a special array-like object that contains all function arguments (actual parameters).
Actually, last two lines are not going to work, because concat isn't a plain function, it's a method of Array objects and therefore a member of Array.prototype structure. We have to tell the JS engine where to find concat. We can use Array.prototype directly:
var combined = Array.prototype.concat.apply([], arguments);
or create a new, unrelated, array object and pull concat from there:
var combined = [].concat.apply([], arguments);
This prototype method is slightly more efficient (since we're not creating a dummy object), but also more verbose.
Anyways, the first step is now complete. To eliminate duplicates, we use the following method:
combined.filter(function(elem, index) {
return combined.indexOf(elem) === index;
})
For explanations and alternatives see this post.
Finally, we get rid of the temporary variable (combined) and chain "combine" and "dedupe" calls together:
return [].concat.apply([], arguments).filter(function(elem, index, self) {
return self.indexOf(elem) === index;
});
using the 3rd argument ("this array") of filter because we don't have a variable anymore.
Simple, isn't it? ;) Let us know if you have questions.
Finally, a small exercise if you're interested:
Write combine and dedupe as separate functions. Create a function compose that takes two functions a and b and returns a new function that runs these functions in reverse order, so that compose(a,b)(argument) will be the same as b(a(argument)). Replace the above definition of unite with unite = compose(combine, dedupe) and make sure it works exactly the same.
You can also try this :
var Data = [[1, 2, 3], [5, 2, 1, 4], [2, 1], [6, 7, 8]]
var UniqueValues = []
for (var i = 0; i < Data.length; i++) {
UniqueValues = [...new Set(UniqueValues.concat(Data[i]))]
}
console.log(UniqueValues)
I did the following in javascript:
var arr1 =[1,2,3,4];
var arr2 =["ac", "bc", "ad", "e"];
var result = arr1 .sort(function(i, j){return arr2[i].localeCompare(arr2[j])})
document.write(result );
my intention was to sort array1 based on array2. I was expecting the result to be 1,3,2,4, but as it turns out it is 2,1,3,4 can anyone figure out why? Thanks
Arrays are 0-indexed, so your sort function starts comparing with the second and all the way through the fifth; ignoring the first element and the fact that there is no 5th element.
Inserting a -1 in the sort function should fix it:
arr1.sort(function(i, j){
return arr2[i-1].localeCompare(arr2[j-1])
});
The result is indeed [1, 3, 2, 4]
The arguments in the sort method are the array items, not their index, so you need to find the index based on the items, assuming the values are unique.
Basic example will be:
var result = arr1.sort(function(i, j) {
return arr2[Find(arr1, i)].localeCompare(arr2[Find(arr1, j)]);
});
Where the Find function can be:
function Find(arr, key) {
for (var i = 0; i < arr.length; i++)
if (arr[i] == key)
return i;
return -1;
}
Test case: http://jsfiddle.net/tqQDJ/