remove elements from an array javascript - javascript

this the forth project from the odin project, all tests passed but the fifth one which required removing all elements failed and when i run the code it returns an array with half elements in original array before mutating.
I don't know why IT DOESN'T RETURN AN EMPTY ARRAY.in the fifth test.
const removeFromArray = function (array, ...deleteElement) {
for (let i = 0; i < array.length; i++) {
if (array.includes(deleteElement[i])) {
array.splice(array.indexOf(deleteElement[i]), 1);
}
}
return array;
};
const randomArray = [1, 2, 3, 4];
console.log(removeFromArray(randomArray, 1, 2, 3, 4));
and this the test
const removeFromArray = require('./removeFromArray')
describe('removeFromArray', () => {
test('removes a single value', () => {
expect(removeFromArray([1, 2, 3, 4], 3)).toEqual([1, 2, 4]);
});
test('removes multiple values', () => {
expect(removeFromArray([1, 2, 3, 4], 3, 2)).toEqual([1, 4]);
});
test('ignores non present values', () => {
expect(removeFromArray([1, 2, 3, 4], 7, "tacos")).toEqual([1, 2, 3, 4]);
});
test('ignores non present values, but still works', () => {
expect(removeFromArray([1, 2, 3, 4], 7, 2)).toEqual([1, 3, 4]);
});
test.skip('can remove all values', () => {
expect(removeFromArray([1, 2, 3, 4], 1, 2, 3, 4)).toEqual([]);
});
test.skip('works with strings', () => {
expect(removeFromArray(["hey", 2, 3, "ho"], "hey", 3)).toEqual([2, "ho"]);
});
test.skip('only removes same type', () => {
expect(removeFromArray([1, 2, 3], "1", 3)).toEqual([1, 2]);
});
});

In the for you are looping trough the wrong element. It should be deleteElement.length, not array.length
const removeFromArray = function(array, ...deleteElement) {
for (let i = 0; i < deleteElement.length; i++) {
if (array.includes(deleteElement[i])) {
array.splice(array.indexOf(deleteElement[i]), 1);
}
}
return array;
};
const randomArray = [1, 2, 3, 4];
console.log(removeFromArray(randomArray, 1, 2, 3, 4));
EDIT:
although this will not work correctly when there are 2 same element to delete in the array. Here is another implementation which is deleting all matched indexes and not only the first one.
const removeFromArray = function(array, ...deleteElement) {
for (let i = 0; i < deleteElement.length; i++) {
let foundIndex = array.indexOf(deleteElement[i]);
while (foundIndex !== -1) {
array.splice(foundIndex, 1);
foundIndex = array.indexOf(deleteElement[i]);
}
}
return array;
};
const randomArray = [1, 2, 3, 1, 4];
console.log(removeFromArray(randomArray, 1));

Your function iterates over the wrong variable (array.length)
const removeFromArray = function (array, ...deleteElement) {
for (let element of deleteElement) {
if (array.includes(element)) {
array.splice(array.indexOf(element), 1);
}
}
return array;
};

The problem is that the for loop is using the array.length property dynamically, which means it is going to be evaluated every time you pass the loop, so since you are removing the elements from the original array, this value is not constant and at some point the array length value and the i counter will get out of sync.
You just need to save the array length value in a variable so it stays the same after you remove the objects from the array.
var arrLen = array.length;
for (let i = 0; i < arrLen; i++) {
...
}
Update:
Pasting the full code here plus a live sample:
https://jsfiddle.net/bcngr/sbk1yhmn/
const removeFromArray = function (array, ...deleteElement) {
var arrLen = array.length; // Save the length value before any modification to the array
for (let i = 0; i < arrLen; i++) { // use the variable instead of array.length
if (array.includes(deleteElement[i])) {
array.splice(array.indexOf(deleteElement[i]), 1);
}
}
return array;
};
const randomArray = [1, 2, 3, 4];
console.log(removeFromArray(randomArray, 1, 2, 3, 4));

Related

Why this rest parameter is usefull here?

I have two solutions for the same problem (both work), one is mine and the other one is from the internet.
In the internet's solution they add the variable modifiedArray. What is the point of doing that?
In the internet's solution, is the [...arr] in the modifiedArray variable not the same that the argument arr on the function removeFromArray()?
My solution:
const removeFromArray = function(arr, ...Args) {
for (i = 0; i <= Args.length; ++i) {
if (arr.includes(Args[i])) {
arr.splice(arr.indexOf(Args[i]), 1)
}
}
return arr;
}
module.exports = removeFromArray
Internet's solution:
const removeFromArray = function(arr, ...Args) {
let modifiedArray = [...arr]
for (i = 0; i < arr.length; ++i) {
if (modifiedArray.includes(Args[i])) {
modifiedArray.splice(modifiedArray.indexOf(Args[i]), 1)
}
}
return modifiedArray;
}
module.exports = removeFromArray
Test for both answers:
const removeFromArray = require('./removeFromArray')
describe('removeFromArray', function() {
it('removes a single value', function() {
expect(removeFromArray([1, 2, 3, 4], 3)).toEqual([1, 2, 4]);
});
it('removes multiple values', function() {
expect(removeFromArray([1, 2, 3, 4], 3, 2)).toEqual([1, 4]);
});
it('ignores non present values', function() {
expect(removeFromArray([1, 2, 3, 4], 7, "tacos")).toEqual([1, 2, 3, 4]);
});
it('ignores non present values, but still works', function() {
expect(removeFromArray([1, 2, 3, 4], 7, 2)).toEqual([1, 3, 4]);
});
it('can remove all values', function() {
expect(removeFromArray([1, 2, 3, 4], 1, 2, 3, 4, 5)).toEqual([]);
});
it('works with strings', function() {
expect(removeFromArray(["hey", 2, 3, "ho"], "hey", 3)).toEqual([2, "ho"]);
});
it('only removes same type', function() {
expect(removeFromArray([1, 2, 3], "1", 3)).toEqual([1, 2]);
});
});
The difference between your code and the other one, is that yours changes the arr parameter directly whereas the other first makes a copy of the array, and then modifies that copy.
When you pass an array to a function, you are actually passing a reference to that array, not a copy of it. This means that when you modify arr directly, you are also modifying the source array.
Here is a nice example:
const removeFromArray1 = function(arr, ...Args) {
for (i = 0; i <= Args.length; ++i) {
if (arr.includes(Args[i])) {
arr.splice(arr.indexOf(Args[i]), 1)
}
}
return arr;
}
const removeFromArray2 = function(arr, ...Args) {
let modifiedArray = [...arr]
for (i = 0; i < arr.length; ++i) {
if (modifiedArray.includes(Args[i])) {
modifiedArray.splice(modifiedArray.indexOf(Args[i]), 1)
}
}
return modifiedArray;
}
const arr1 = [1, 2, 3, 4];
console.log('Returned array 1', removeFromArray1(arr1, 3));
console.log('Source array 1', arr1);
const arr2 = [1, 2, 3, 4];
console.log('Returned array 2', removeFromArray2(arr2, 3));
console.log('Source array 2', arr2);
Here you can see that arr1 is modified after calling removeFromArray1, but arr2 is not modified after calling removeFromArray2. Changing the source array might have odd side-effects if you try to use the initial array expecting it not to be changed.
let modifiedArray = [...arr] is a simple way to make a shallow copy of the array.
The second solution is nearer to functional programming pattern (pure function).
In functional programming you dont push or delete items into existing arrays or objects.
You would rather create a new array with all the same items as the original array, Then you modify the duplicate and return it. The duplication is done by
let modifiedArray = [...arr]
The concept is described as pure Function. A function should not change anything outside the function. No side effects.

How can I reverse the "includes()" method to "not-includes()" and retrieve the not included value?

Im trying to retrieve the not included value in the second array, by using the following code:
function diffArray(arr1, arr2) {
var newArr = [];
for (let i of arr1) {
if (arr2.includes(i)) {
newArr.push(i)
}
}
return newArr
}
console.log(
diffArray([1, 2, 3, 5], [1, 2, 3, 4, 5])
)
Is there any way I can use another method to do this. I tried indexOf but I don't want the index.
Thank you
You can use filter():
let arr1 = [1, 2, 3, 5];
let arr2 = [1, 2, 3, 4, 5];
let result = arr2.filter(a2 => !arr1.includes(a2));
console.log(result);
if (!arr2.includes(i)) {
newArr.push(i)
}
! means not
You could always use else as well, but it's more lines of code:
if (arr2.includes(i)) {
// newArr.push(i)
} else {
newArr.push(i);
}
const a1 = [1, 2, 3, 4, 5];
const a2 = [1, 2, 3, 5];
function diffArray(arr1, arr2) {
const frequencies = arr1.concat(arr2).reduce((frequencies, number) => {
const frequency = frequencies[number];
frequencies[number] = frequency ? frequency + 1 : 1;
return frequencies;
}, {});
return Object.keys(frequencies).filter(number => frequencies[number] === 1);
}

Error using Javascript filter() function in freecodecamp

function destroyer(arr) {
var arry=[];
for(var i=1;i<arr.length;i++)
{
arr[0] = arr[0].filter(cc => cc != arr[i]);
}
return arry;
}
destroyer([1, 2, 3, 1, 2, 3], 2, 3);
I basically have to return all the elements of the first sub-array which arent present in the rest of array.
Its displaying "arr[0].filter isnt a function.
destroyer([1, 2, 3, 1, 2, 3], 2, 3) should return [1, 1].
I basically have to return all the elements of the first sub-array which arent present in the array.
You aren't doing anything with the other arguments provided to the destroyer function - you have to test those arguments against arr, you shouldn't be testing arr against itself.
function destroyer() {
const [arr, ...excludeArr] = arguments;
return arr.filter(elm => !excludeArr.includes(elm));
}
console.log(
destroyer([1, 2, 3, 1, 2, 3], 2, 3)
);
function destroyer(arr, x, y) {
return arr.filter(item => (item != x) && (item != y))
}
console.log(destroyer([1, 2, 3, 1, 2, 3], 2, 3))
const destroyer = (arr, ...args) => {
return arr.filter(item => args.indexOf(item) < 0);
};
const result = destroyer([1, 2, 3, 1, 2, 3], 2, 3);
console.log(result);
as you said you should filter first subarray but you are sending only 1 array with int values and other values as argument, the actual usage of this function should be
function destroyer(arr) {
for(var i = 1; i < arr.length; i++)
{
arr[0] = arr[0].filter(cc => cc != arr[i]);
}
return arr[0];
}
destroyer([[1, 2, 3, 1, 2, 3], 2, 3]);
You forgot to call your method using the array. you have to surround it with another pair of [].
You can simply return the filtered arr[0] to get what you want.
function destroyer(arr) {
for (var i = 1; i < arr.length; i++) {
arr[0] = arr[0].filter(cc => cc != arr[i]);
}
return arr[0];
}
console.log(destroyer([[1, 2, 3, 1, 2, 3], 2, 3]));

Move object from one array to another

i have one object that one of the properties is an array of objects, the idea is to move objects from that array to no new one if one condition is true.
public $onInit(): void {
this.getTicket();
}
public ticket: any; // Object with the array
public comments: any = []; // New array to move the elements
public getTicket(): void {
this.ticketService
.getTicketComplete(this.$stateParams.ticketID)
.then((response: any) => {
this.ticket = response;
this.stringToDate(this.ticket);
this.ticket.messages.forEach((elem, index) => {
if (elem.type === "comment") {
this.ticket.messages.splice(index, 1);
this.comments.push(elem);
}
});
console.log(this.ticket);
});
}
the problem that i have is the next one:
the array has to types of objects, messages and comments, if the array has 2 messages and 3 comments it should push to the new array 3 comments and leave 2 messages, but is moving only 2 comments.
Any idea. Thanks for your help.
This is the way You do it:
var array1 = [1, 2, 3, 4, 5];
var array2 = [];
array1.forEach(function(elem, index) {
array1.splice(index, 1);
array2.push(elem);
});
console.log(array1); //[2, 4]
console.log(array2); //[1, 3, 5]
This is an example of how it can be done:
var array1 = [1, 2, 3, 4, 5];
var array2 = [];
for(var i = 0; i < array1.length; i++) {
array2.push(array1[i]);
array1.splice(i, 1);
i--; //decrement i IF we remove an item
}
console.log(array1); //[]
console.log(array2); //[1, 2, 3, 4, 5]
Specific use-case for you:
let messages = this.ticket.messages;
for(let i = 0; i < messages.length; i++) {
let message = messages[i];
if (message.type === "comment") {
this.comments.push(message);
messages.splice(i, 1);
i--;
}
}
You are removing elements as you loop through your array - this is never a good idea. A better way to solve this issue is to add them to this.comments first and when the foreach is finalized, start looping over this.comments and remove those that are in this array out of the messages.
Here we go the async way baby:
var array1 = [1, 2, 3, 4, 5];
var array2 = [];
async function fillArray() {
array1.forEach(function(elem, index) {
console.log("elem: ", elem)
array2.push(elem);
})
}
async function emptyArray(){
fillArray().then(() =>{
array1.length = 0;
})
}
emptyArray().then(() => {
console.log("array1: ", array1); //[]
console.log("array2: ", array2); //[1, 2, 3, 4, 5]
})
An another great move, conditional way, cheer:
var array1 = [1, 2, 3, 4, 5];
var array1Length= array1.length;
var array2 = [];
array1.forEach((elem, index) => {
array2.push(elem);
if(array2.length === array1Length) array1.length=0
})
console.log("array1: ", array1); //[]
console.log("array2: ", array2); //[1, 2, 3, 4, 5]

Counting the same nearest values in Javascript array

Given this array:
[1, 1, 2, 1, 1, 1, 2, 3, 4, 4, 4, 6, 4, 4]
How can I efficently count the nearest same elements in array, the result I would expect is:
1 => 2,
2 => 1,
1 => 3,
2 => 1,
3 => 1,
4 => 3,
6 => 1,
4 => 2
I don't know how to formulate correctly the question but I think the example is pretty clear.
I tried using reduce to make more compact and elegant but I always get a value with total number of same value in array.
let result = testArray.reduce((allValues, value) => {
if(value in allValues){
allValues[value]++;
} else {
allValues[value] = 1;
}
return allValues;
}, {});
You could check the last element and if equal, increment count, if not push a new object to the result set.
var array = [1, 1, 2, 1, 1, 1, 2, 3, 4, 4, 4, 6, 4, 4],
count = array.reduce((r, a, i, aa) => {
if (aa[i - 1] === a) {
r[r.length - 1].count++;
} else {
r.push({ value: a, count: 1 });
}
return r;
}, []);
console.log(count);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Another solution using Array.prototype.reduce and a hash table - see demo below:
var array = [1, 1, 2, 1, 1, 1, 2, 3, 4, 4, 4, 6, 4, 4];
var result = array.reduce(function(hash){
return function(p,c,i){
hash[c] = (hash[c] || 0) + 1;
if(hash.prev && (hash.prev !== c || i == array.length - 1)) {
let obj= {};
obj[hash.prev] = hash[hash.prev];
delete hash[hash.prev];
p.push(obj);
}
hash.prev = c;
return p;
}
}(Object.create(null)),[]);
console.log(result);
.as-console-wrapper{top:0;max-height:100%!important;}
While Array.prototype.reduce is a nice function, it does have some compatibility issues.
Here is a solution using old school for loops:
var arr = [1, 1, 2, 1, 1, 1, 2, 3, 4, 4, 4, 6, 4, 4];
var results = [];
for (var indexA = 0; indexA < arr.length; indexA++) {
var a = arr[indexA];
if (results.length > 0) {
if (a == results[results.length - 1]["value"]) {
continue;
}
}
var r = { value: a, index: indexA, count: 0 };
for (var indexB = indexA; indexB < arr.length; indexB++) {
var b = arr[indexB];
if (a != b) {
break;
}
r.count++;
}
results.push(r);
}
console.log(results);
The posted solutions are fine but as noted by #Emil S. Jørgensen there might be compatibility issues using Array.reduce. Also, the suggested old school solution by #Emil S. Jørgensen uses two loops. If you want to have a slightly more efficient,simple and straightforward solution which will work in all browsers then use:
var arr = [1, 1, 2, 1, 1, 1, 2, 3, 4, 4, 4, 6, 4, 4];
var result = [];
var current = arr[0];
var count = 1;
for (var i = 1; i <= arr.length; i++)
{
if(arr[i] === current)
{
count+= 1;
}
else
{
var newObj = {};
newObj[current] = count;
result.push(newObj);
current = arr[i];
count = 1;
}
}
console.log(result); //prints the solution

Categories

Resources