Extending Array.prototype crashes - javascript

I was trying to solve the following problem which I got on a blog but the program crashes. What could be the reason? and is there any means of solving it? I have read warnings not to extend builtin objects, if that's the case, what could be the reason associated with this specific example.
const a = [1, 2, 3, 4, 5];
//this is what I tried
Array.prototype.multiply = function() {
for (m of this) this.push(m * m);
}
a.multiply(); //this should not be changed
console.log(a); // [1, 2, 3, 4, 5, 1, 4, 9, 16, 25] (expected output)

When you push the value to same array during loop, you end up in infinite loop, create a temp array push value to it, in the end add it to this
const a = [1, 2, 3, 4, 5];
//this is the what I tried
Array.prototype.multiply = function() {
let newArr = []
for (const m of this) {
newArr.push(m * m)
}
this.push(...newArr)
}
a.multiply();
console.log(a);
That being said you should not override the prototype simply use a function and pass the parameters
const a = [1, 2, 3, 4, 5];
function multiply(arr) {
return [...arr, ...arr.map(a => a * a)]
}
console.log(multiply(a));

Pushing a new value into an array in the middle of a for ... of loop creates an infinite loop as the loop includes the new values. You can use forEach instead as that ignores the new values added:
const a = [1, 2, 3, 4, 5];
Array.prototype.multiply = function() {
this.forEach(v => this.push(v*v));
}
a.multiply(); //this should not be changed
console.log(a); // [1, 2, 3, 4, 5, 1, 4, 9, 16, 25] (expected output)

Related

How to find symmetrical difference using JavaScript?

I've been working on an algorithm that will find the symmetric difference of two arrays (i.e. only items that are in 1 of the 2 arrays, but not both). I've come up with the following so far:
function diffArray(arr1, arr2) {
let newArr1 = arr1.slice();
let newArr2 = arr2.slice();
if (newArr1.length > newArr2.length) {
return newArr1.filter((item) => !(item in newArr2));
} else {
return newArr2.filter((item) => !(item in newArr1));
}
};
const result = diffArray([1, 2, 3, 5], [1, 2, 3, 4, 5]);
console.log(result);
But when testing with diffArray([1, 2, 3, 5], [1, 2, 3, 4, 5]) the output is [4, 5] instead of just [4]. The issue seems to happen with whatever the last value of either array is, but I can't seem to figure out what about my code is causing the issue. Thanks in advance for any help.
The in operator checks if the value on the left matches a property name on the right.
In you want to check if one of the property values is in an array, use the includes method.
function diffArray(arr1, arr2) {
let newArr1 = arr1.slice();
let newArr2 = arr2.slice();
if (newArr1.length > newArr2.length) {
return newArr1.filter((item) => !newArr2.includes(item));
} else {
return newArr2.filter((item) => !newArr1.includes(item));
}
};
const result = diffArray([1, 2, 3, 5], [1, 2, 3, 4, 5]);
console.log(result);
Updating the answer for another condition
const result = diffArray([1, 2, 3, 4], [1, 2, 3, 5]);
function diffArray(arr1, arr2){
const diff1 = arr1.filter(item => !arr2.includes(item)); // if item not available, the value will be filtered
const diff2 = arr2.filter(item => !arr1.includes(item));// if item not available, the value will be filtered
return diff1.concat(diff2);
}
// const result = diffArray([1, 2, 3, 5], [1, 2, 3, 4, 5]);
const result = diffArray([1, 2, 3, 4], [1, 2, 3, 5]); // This condition will fail on the above code
console.log(result);

Check to see if an array contains all elements of another array, including whether duplicates appear twice

I need to check whether one array contains all of the elements of another array, including the same duplicates. The second array can have extra elements. I'm using every...includes, but it's not catching that the second array doesn't have the right duplicates.
For example:
const arr1 = [1, 2, 2, 3, 5, 5, 6, 6]
const arr2 = [1, 2, 3, 5, 6, 7]
if(arr1.every(elem => arr2.includes(elem))){
return true // should return false because arr2 does not have the same duplicates
}
Thanks!
Edit: arr1 is one of many arrays that I am looping through which are coming out of a graph traversal algorithm, so I'd like to avoid restructuring them into an object to create a dictionary data structure if possible.
Try creating this function:
function containsAll (target, toTest) {
const dictionary = {}
target.forEach(element => {
if (dictionary[element] === undefined) {
dictionary[element] = 1;
return;
}
dictionary[element]++;
});
toTest.forEach(element => {
if (dictionary[element] !== undefined)
dictionary[element]--;
})
for (let key in dictionary) {
if (dictionary[key] > 0) return false;
}
return true;
}
Then invoke it like this:
const arr1 = [1, 2, 2, 3, 5, 5, 6, 6]
const arr2 = [1, 2, 3, 5, 6, 7]
console.log(containsAll(arr1, arr2)) // returns false
const arr1 = [1, 2, 2, 3, 5, 5, 6, 6];
//const arr2 = [1, 2, 3, 5, 6, 7];
const arr2 = [1, 2, 2, 3, 5, 5];
let includesAll1 = true;
let includesAll2 = true;
const checkObj1 = {
};
const checkObj2 = {
};
arr1.forEach((el)=> {
if(checkObj1[el] === undefined) {
checkObj1[el] = 1;
} else {
checkObj1[el]++;
}
});
arr2.forEach((el)=> {
if(checkObj2[el] === undefined) {
checkObj2[el] = 1;
} else {
checkObj2[el]++;
}
});
const check1Keys = Object.keys(checkObj1);
const check2Keys = Object.keys(checkObj2);
if(check1Keys.length > check2Keys.length) {
includesAll2 = false;
check2Keys.forEach((key)=> {
const value1 = checkObj1[key];
const value2 = checkObj2[key];
if(!arr1.includes(parseInt(key)) || value1 != value2) {
includesAll1 = false;
}
});
} else {
includesAll1 = false;
check1Keys.forEach((key)=> {
const value1 = checkObj1[key];
const value2 = checkObj2[key];
console.log(value1, value2, key);
if(!arr2.includes(parseInt(key)) || value1 != value2) {
includesAll2 = false;
}
});
}
console.log(includesAll1);
console.log(includesAll2);
Does this solve your problem?
const arr = [1, 2, 3, 5, 6, 7, 2, 10, 2, 3, 2];
const subArr = [1, 2, 2, 3, 2]
const contains = subArr.every(num => subArr.filter(n => n == num).length <= arr.filter(n => n== num).length);
console.log(contains);
You indicate order does not matter in your comments. That makes this very simple.
Sort both arrays
Check if corresponding elements are equal
consider errors associated with sparse or short arrays
Use .reduce() to boil it down to a single result
So this really comes down to a single statement once the arrays are sorted:
matcher.reduce((acc, value , idx)=>matcher[idx] === test[idx], false);
You also mentioned testing this against many arrays. So the full example below does that for demo purposes.
let toMatch = [1, 2, 2, 3, 5, 5, 6, 6]
let arrayOfArrays = [[1,2],[1, 2, 3, 5, 6, 7, 3, 9, 8, 2, 7],[1, 2, 3, 3, 6, 7],[1, 3, 3, 5, 6, 7],[1, 2, 3, 5, 6, 6], [3,5,2,1,6,2,5,6]];
let toMatchSorted = toMatch.slice().sort();
arrayOfArrays.forEach(arr=>{
let toTestSorted = arr.slice().sort();
let out = toMatchSorted.reduce((acc, value , idx)=>toMatchSorted[idx] === toTestSorted[idx], false);
console.log(`Input: ${arr}, Result: ${out}`);
});

Javascript sort() is not sorting correctly

Trying to sort an array and sort() is switching just 1 value and adding the largest to the middle of the array. Could anyone help me understand what is happening?
My Results that I get are:
Original Array: [2643832795, 1, "3", 10, 3, 5]
New Array: [1, 10, 2643832795, 3, 3, 5]
let startArray = [2643832795, 1, "3", 10, 3, 5];
function bigSorting(unsorted) {
let newArray = [];
unsorted.forEach(el => {
if(typeof(el) == "string")
{ newArray.push(parseInt(el)) }
else { newArray.push(parseInt(el)) }
});
newArray.sort();
console.log(startArray);
console.log(newArray);
}
bigSorting(startArray);
It's using lexical comparison of strings. You can use a comparison function that coerces to numbers and subtracts for comparison
let startArray = [2643832795, 1, "3", 10, 3, 5];
function bigSorting(unsorted) {
let newArray = [];
unsorted.forEach(el => {
if(typeof(el) == "string")
{ newArray.push(parseInt(el)) }
else { newArray.push(parseInt(el)) }
});
newArray.sort();
console.log(newArray);
console.log(startArray);
console.log(unsorted.sort((x,y)=>x-y))
}
bigSorting(startArray);
you're very close :-)
let startArray = [2643832795, 1, "3", 10, 3, 5];
function bigSorting(unsorted) {
const newArray = unsorted.map(x => parseInt(x, 10));
newArray.sort((a, b) => a > b ? 1 : -1);
return newArray;
}
console.log(startArray);
console.log(bigSorting(startArray));
You're getting a lexicographical sort (e.g. convert objects to strings, and sort them in dictionary order), which is the default sort behavior in Javascript: Refer Docs
In ES6, you can simplify this with arrow functions:
unsorted.sort((a, b) => a - b);
So this should be like:
let startArray = [2643832795, 1, "3", 10, 3, 5];
function bigSorting(unsorted) {
let newArray = [];
unsorted.forEach(el => {
if(typeof(el) == "string")
{ newArray.push(parseInt(el)) }
else { newArray.push(parseInt(el)) }
});
newArray.sort((a, b) => a - b);
console.log(startArray);
console.log(newArray);
}
It’s sorting as it should. It sorts accordingly as it would sort a list of strings.
var list = [2643832795, 1, "3", 10, 3, 5];
When you write list.sort() it just sorts as would sort any list of strings according to each character's Unicode code point value.
So, 1 comes first then 2, 3 and so on. So even if it's 2643832795, it would come before 3.
If you want to sort this treating them as integers, you should supply a callback something like this:
var list = [2643832795, 1, "3", 10, 3, 5];
var newList = list.map(i=>parseInt(i)); //Convert the strings in the list into integers
newList.sort((a, b) => a-b);
console.log(newList)
Try this, hope it can help you.
var numbers = [2, 5, 8, 7, 1, 6, 10, 3, 4, 9];
var asc = numbers.sort((a, b) => a - b);
console.log(asc); // Output: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]. Numeric sort
numbers = [2, 5, 8, 7, 1, 6, 10, 3, 4, 9];
var des = numbers.sort((a, b) => b - a);
console.log(des); // Output: [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]. Numeric descending sort

JavaScript reference drop

I'm creating a module that extends existing application. I've received a variable device and I want to create myDevice that will always hold the same data. Lets say that data is contained in an array:
https://jsfiddle.net/hmkg9q60/2/
var device = {
name: "one",
data: [1, 2, 3]
};
var myDevice = {
name: "two",
data: []
};
myDevice.data = device.data; // Assign array reference
device.data.push(4); // Push works on array reference
console.log(device.data); // [1, 2, 3, 4]
console.log(myDevice.data); // [1, 2, 3, 4] - ok
device.data = [0, 0, 0]; // A new array is assigned to 'device'
// and 'myDevice' reference stays with old array
console.log(device.data); // [0, 0, 0]
console.log(myDevice.data); // [1, 2, 3, 4] - I would like to get [0,0,0]
What I would like to obtain is to be sure, that myDevice will always hold the same data as device, even if someone decides to use the assign operator somewhere in the application. I don't want to clone myDevice because I want to hold "name" and other properties.
Is there a way in JavaScript to create such reference to an object field?
You could use getters and setters, and read and assign directly from and to the device object.
var device = {
name: "one",
data: [1, 2, 3]
};
var myDevice = {
name: "two",
get data() { return device.data; },
set data(newdata) { device.data = newdata; },
};
console.log(myDevice.data);
device.data = [4,5,6];
console.log(myDevice.data);
I would use a getter
NOTE: more elegant ES6 solution
var device = {
name: "one",
data: [1, 2, 3]
};
var myDevice = {}
myDevice.getData = () => device.data;
myDevice.bla = "bla";
device.data.push(4); // Push works on array reference
console.log(device.data); // [1, 2, 3, 4]
console.log(myDevice.getData()); // [1, 2, 3, 4] - ok
device.data = [0, 0, 0]; // A new array is assigned to 'device'
// and 'myDevice' reference stays with old array
console.log(device.data); // [0, 0, 0]
console.log(myDevice.getData()); // [1, 2, 3, 4] - I would like to get [0,0,0]

Convert an Array to unique values only while maintaining the correct sequence

I have the following code:
function uniteUnique(arr) {
//Create a single Array of value
arr = arguments[0].concat(arguments[1], arguments[2]);
//Reduce the Array to unique values only
arr = arr.reduce((pre, curr) => {
//Some function to reduce values
});
return arr;
}
uniteUnique([1, 3, 2], [5, 2, 1, 4], [2, 1]);
The goal is to produce a single Array containing only unique values while maintaining the order.
Currently it returns:
[1, 3, 2, 5, 2, 1, 4, 2, 1]
I'm wanting to reduce this to:
[1, 3, 2, 5, 4]
You can use Set for that:
function uniteUnique(...args) {
return [...new Set([].concat(...args))];
}
var u = uniteUnique([1, 3, 2], [5, 2, 1, 4], [2, 1]);
console.log(u);
It maintains insertion order, and by nature only contains unique values.
In ES5 you could do it by maintaining the used values as properties of a temporary object, while building the result array:
function uniteUnique(/* args */) {
return [].concat.apply([], arguments).reduce(function (acc, v) {
if (!acc[0][v]) acc[0][v] = acc[1].push(v); // assigns new length, i.e. > 0
return acc;
}, [ Object.create(null), [] ])[1];
}
var u = uniteUnique([1, 3, 2], [5, 2, 1, 4], [2, 1]);
console.log(u);
You can use the Set object since it already keeps your values unique in one object:
const mySet = new Set([1, 3, 2, 5, 2, 1, 4, 2, 1]);
// returns: Set { 1, 3, 4, 5 };
const arrayUniques = [...mySet];
console.log(arrayUniques);
// returns: [1, 3, 4, 5];

Categories

Resources