The importance of arguments object in JavaScript - javascript

I am very new to arguments object concept in JavaScript and trying to understand the concept from this tutorial.
The below example from the doc explains as follows.
function func1(a, b, c) {
console.log(arguments[0]);
// expected output: 1
console.logr(arguments[1]);
// expected output: 2
console.log(arguments[2]);
// expected output: 3
}
func1(1, 2, 3);
But I am unable to understand the importance of this concept and in which scenario we mostly use such concept?
Excuse me for question format issues as I am typing in mobile.

The arguments object is useful in cases you have possibly a variable number of arguments:
function sumAll() {
var res = 0;
for (var i = 0; i < arguments.length; i++) {
res += arguments[i];
}
return res;
}
console.log(sumAll(3)); // 3
console.log(sumAll(3, 4, 5)); // 12
However, since ES6, a version of javascript standardized in 2015 ( also called ES2015), you can use the rest parameter syntax to simplify this operation:
function sumAll(...args) {
var res = 0;
for (var i = 0; i < args.length; i++) {
res += args[i];
}
return res;
}
console.log(sumAll(3)); // 3
console.log(sumAll(3, 4, 5)); // 12
The arguments object is therefore no longer necessary.

function summIt(){
let result = 0;
let args = [...arguments];
args.forEach(el => result += el);
return result;
}
console.log(summIt(4,7,4,8,9,3,3)); //38
OR
const summ = (...args) => {
let result = 0;
Array.from(args).forEach(el => result += el);
return result;
}
console.log(summ(4,7,4,8,9,3,3)); //38

Related

Function that sums up all integers from a deeply nested array

I have an issue here, I'm trying to create a function that sums up all integers from a deeply nested array, but it's failing an unit test, which means something is not right.
Here is my function:
export const arraySum = (arr) => {
let sum = 0;
for (let i = 0; i < arr.length; i++) {
if (typeof arr[i] === "number") sum = sum + arr[i];
else if (Array.isArray(arr[i])) sum = sum + arraySum(arr[i]);
}
return sum;
};
And here is my unit test which is failing:
test("it should sum up from deeply nested arrays", () => {
type ValueOrArray = number | Array<ValueOrArray>;
const createDeeplyNestedArray = (depth: number): ValueOrArray => {
let retval: ValueOrArray = [1];
for (let i = 0; i < depth - 1; i++) {
retval = [1, retval];
}
return retval;
};
const NUMBER_OF_ELEMENTS = 100000;
const arr = createDeeplyNestedArray(NUMBER_OF_ELEMENTS);
expect(arraySum(arr)).toEqual(NUMBER_OF_ELEMENTS);
});
Function memory is stored on something called a "call stack". Whenever you call a function, all of its variables are allocated and pushed onto the 'stack' and when the function returns, they are popped off the stack. Given the following code:
const a = () => {
}
const b = () => {
a()
// some code
}
const c = () => {
b()
}
c()
When c is called, your call stack will contain all the memory for variables used in c. When c calls b, all the memory for variables used in b are added to the stack. When b calls a all the memory for variables used in a are added to the stack. When a finishes executing (so when you get to 'some code'), variables related to a are deallocated and removed from the stack.
The problem you have here is that every time your function recursively calls itself, more memory is being allocated onto the stack. to stop this kind of code using up all the system memory, the runtime limits how big the stack can get - which is why you are hitting this error.
To pass this test, you need a solution which doesn't call itself every time it hits an array within an array. Here's my solution, effectively using an array as a buffer; each time I hit a nested array I add it to the buffer. Once I finish processing the outer array, I then check if there is any arrays left in the buffer.
export const arraySum = (arr) => {
let sum = 0;
const buffer = [arr];
while (buffer.length > 0) {
const next = buffer.shift();
for (let i = 0; i < next.length; i++) {
if (typeof next[i] === "number") sum = sum + next[i];
else if (Array.isArray(next[i])) buffer.push(next[i]);
}
}
return sum;
};
That doesn't work bc like Keith said you are reaching the maximum call stack size.
RangeError: Maximum call stack size exceeded is a type of error thrown in JavaScript when a function call is made that exceeds the program's stack size. The call stack is a data structure that keeps track of which functions have been called, and in what order.
Maybe you can try to solve it in a iterative way like this:
const arraySum = (arr) => {
if (!Array.isArray(arr)) return 0;
let sum = 0;
while (Array.isArray(arr)) {
let arrCopy = [];
for (let i = 0; i < arr.length; i++) {
if (typeof arr[i] === "number") sum = sum + arr[i];
else if (Array.isArray(arr[i])) arrCopy = arrCopy.concat(arr[i]);
}
arr = arrCopy.length > 0 ? arrCopy : null;
}
return sum;
};
We can use a stack or queue in place of recursion. Also, order doesn't matter.
function f(A) {
let sum = 0;
let stack = [A];
while (stack.length > 0) {
stack.pop().forEach(e => {
if (Array.isArray(e))
stack.push(e);
else if (typeof e === "number")
sum += e;
});
}
return sum;
}
console.log(f([1,2,[3,[4]]]));
The approach of combining a flat based flattening function which circumvents call stack errors of very deeply nested arrays of a nesting depth close to 10_000 and higher and a reduce based function which does sum-up number types only, does solve the OP's problem. And the following implementation does prove it ...
// Implementation
function flatAlmostInfiniteNestedArray(arr) {
while (arr.length < (arr = arr.flat(1000)).length) {
}
return arr;
}
function totalNestedNumberValues(arr) {
return flatAlmostInfiniteNestedArray(arr)
.reduce((total, value) => (
'number' === typeof value
? total + value
: total
), 0);
}
// Test
const createDeeplyNestedArray = depth => {
let retval = [1];
for (let i = 0; i < depth - 1; i++) {
retval = [1, retval];
}
return retval;
};
const NUMBER_OF_ELEMENTS = 100000;
const arr = createDeeplyNestedArray(NUMBER_OF_ELEMENTS);
console.log(
'(totalNestedNumberValues(arr) === NUMBER_OF_ELEMENTS) ?..',
(totalNestedNumberValues(arr) === NUMBER_OF_ELEMENTS),
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
Edit ... due to following comments
"Very interesting. I hadn't realized that Array.flat was implemented in a way that would lead to a call stack overflow. It makes sense that it would be, but I didn't know, and have never had a real-world structure where it would matter. Thanks!" – Scott Sauyet
"#ScottSauyet ...the poor man's approach of (mis)using flat as kind of a callstack-safe solution looses if it comes to performance ... jsbench.me :: sum up deeply nested array's number values" – Peter Seliger
One of the above linked performance tests features a better, much more performant, stack based, solution for flattening deeply nested arrays which are critical of being natively flattened due to possible overflowing call stacks.
Thus the formerly provided code example would change to ...
// Implementation
function flatCallstackCriticalNestedArray(nested) {
const stack = [nested];
const flat = [];
let value;
while (stack.length) {
if (Array.isArray(value = stack.pop())) {
stack.push(...value);
} else {
flat.push(value);
}
}
return flat;
}
function totalNestedNumberValues(arr) {
return flatCallstackCriticalNestedArray(arr)
.reduce((total, value) => (
'number' === typeof value
? total + value
: total
), 0);
}
// Test
const createDeeplyNestedArray = depth => {
let retval = [1];
for (let i = 0; i < depth - 1; i++) {
retval = [1, retval];
}
return retval;
};
const NUMBER_OF_ELEMENTS = 100000;
const arr = createDeeplyNestedArray(NUMBER_OF_ELEMENTS);
console.log(
'(totalNestedNumberValues(arr) === NUMBER_OF_ELEMENTS) ?..',
(totalNestedNumberValues(arr) === NUMBER_OF_ELEMENTS),
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
Have you considered a using library? This might not be as fast as a vanilla solution, but it should still be plenty fast and much more readable.
.as-console-wrapper {max-height: 100% !important; top: 0}
<script type="module">
import objectScan from 'https://cdn.jsdelivr.net/npm/object-scan#18.3.0/lib/index.min.js';
const createDeeplyNestedArray = (depth) => {
let retval = [1];
for (let i = 0; i < depth - 1; i += 1) {
retval = [1, retval];
}
return retval;
};
const NUMBER_OF_ELEMENTS = 100000;
const arr = createDeeplyNestedArray(NUMBER_OF_ELEMENTS);
const arraySum = objectScan(['**'], {
rtn: 'sum',
filterFn: ({ value }) => typeof value === 'number'
});
console.log(arraySum(arr));
// => 100000
</script>
Disclaimer: I'm the author of object-scan
Note that this will traverse arrays and objects. If you only want to traverse arrays you could use ['**{[*]}'] as search needles.

Nesting dot notation within bracket notation to create nested objects

"Write a function arrayToList that builds up a list structure like"
let LL = { data: 1, next: { data: 2, next: { data: 3, next: null }}};
I understand the typical solution to this problem, where the list must be built from the inside out:
function arrToLList(arr) {
let LList = null;
for (let i = arr.length - 1; i >= 0; i--) {
LList = { data: arr[i], next: LList };
}
return LList;
}
But my initial solution was to brute force it with a typical for loop.
function arrayToLList(arr) {
let d = "data";
let n = "next";
let LList = nextNode();
for (let i = 0; i < arr.length; i++) {
LList[d] = arr[i];
d = "next." + d;
LList[n] = nextNode();
n = "next." + n;
}
function nextNode() {
return {
data: null,
next: null
};
}
return LList;
}
What you want to achieve is possible, but you need to customize the functionality of how getting a property works when you use bracket notation. As you mentioned, using dot notation with bracket notation won't work, you need a way to define this logic yourself. ES6 introduced Proxies which allows you to specify a set method trap for your object. Whenever you set a value on the object, the set method will be called. Using this idea, you can split the dot-notation string by . and traverse the path it returns to get your nested object. Once you have retrieved the nested object, you can set its value.
See example below:
function arrayToLList(arr) {
let d = "data";
let n = "next";
let LList = nextNode();
for (let i = 0; i < arr.length; i++) {
LList[d] = arr[i];
d = "next." + d;
if(i < arr.length-1) // don't add null object to last node
LList[n] = nextNode();
n = "next." + n;
}
function nextNode() {
const obj = {
data: null,
next: null
};
return new Proxy(obj, {
set: function(obj, key, val) {
const path = key.split('.');
const last = path.pop();
for(const prop of path)
obj = obj[prop];
obj[last] = val;
return true;
}
});
}
return LList;
}
console.log(arrayToLList([1, 2, 3]));
However, you don't need to use a proxy. A more straightforward way of doing this would be by creating a method such as setValueByPath(val, obj, strPath) which performs the logic in the proxy for you. Then, instead of setting your object using bracket notation, you simply call the setValueByPath(obj, strPath):
function setValudByPath(val, obj, strPath) { // pefroms same logic from proxy, just using reduce instead
const path = strPath.split('.');
const last = path.pop();
path.reduce((nested, p) => nested[p], obj)[last] = val;
}
function arrayToLList(arr) {
let d = "data";
let n = "next";
let LList = {data: null, next: null};
for (let i = 0; i < arr.length; i++) {
setValudByPath(arr[i], LList, d); // same as LList[d] = arr[i];
d = "next." + d;
if(i < arr.length-1) // don't add null object to last node
setValudByPath({data: null, next: null}, LList, n); // same as: LList[n] = nextNode();
n = "next." + n;
}
return LList;
}
console.log(arrayToLList([1, 2, 3]));
As you are trying to access an objects parameters using strings.
You can't use dot notation with string.
e.g.
let data = {name:'test'};
console.log("data.name");
this is what you're attempting and it will return data.name and not the value test.
you can do the following though: data['name'] so with nested object you can do the following:
LList['next']['next']...['next']['data']
to get the n'th data element.

JavaScript: Higher Order Function that Calls Add Function to Return New Array; Error: 'NaN' elements in output array

I have a function 'sometimes' and want to return a function object from it.
Below is my code:
let add = (a, b) => {
return a + b;
};
myFunc = sometimes(add);
const outputArr = [];
for (let i = 0; i < 3; i++) {
outputArr.push(myFunc(2 + i, 3 + i));
}
function sometimes (inputFunc){
return function (a, b){
return inputFunc()
}
}
outputArr
I expect my outputArr variable to equal:
[5, 7, 9]
Instead mine equals:
[ NaN, NaN, NaN ]
What am I doing wrong?
You are not passing the parameters to the wrapped function. The add function tries to sum two undefined values and the result is NaN (not a number).
You have to pass the parameters to the wrapped function:
return function(a, b) {
return inputFunc(a, b); // <<<
}
Since sometimes is a higher order function, that needs to wrap different functions, with a changing number of parameters, it can't know the implementation of the wrapped function. To ensure that, you should use rest parameters (to collect the parameters to an array) and spread (to convert the array back to parameters) to pass the arguments to the wrapped function.
let add = (a, b) => {
return a + b;
};
myFunc = sometimes(add);
const outputArr = [];
for (let i = 0; i < 3; i++) {
outputArr.push(myFunc(2 + i, 3 + i));
}
function sometimes(inputFunc) {
return function(...args) {
return inputFunc(...args)
}
}
console.log(outputArr);
you can use your code as
function sometimes(a,b){
return a + b;
}
const outputArr = [];
for (let i = 0; i < 3; i++) {
outputArr.push(sometimes(2 + i, 3 + i));
}
console.log(outputArr);
now the output is
[ 5, 7, 9 ]

Convert first N item in iterable to Array

Something similar to question Convert ES6 Iterable to Array. But I only want first N items. Is there any built-in for me to do so? Or how can I achieve this more elegantly?
let N = 100;
function *Z() { for (let i = 0; ; i++) yield i; }
// This wont work
// Array.from(Z()).slice(0, N);
// [...Z()].slice(0, N)
// This works, but a built-in may be preferred
let a = [], t = Z(); for (let i = 0; i < N; i++) a.push(t.next().value);
To get the first n values of an iterator, you could use one of:
Array.from({length: n}, function(){ return this.next().value; }, iterator);
Array.from({length: n}, (i => () => i.next().value)(iterator));
To get the iterator of an arbitrary iterable, use:
const iterator = iterable[Symbol.iterator]();
In your case, given a generator function Z:
Array.from({length: 3}, function(){ return this.next().value; }, Z());
If you need this functionality more often, you could create a generator function:
function* take(iterable, length) {
const iterator = iterable[Symbol.iterator]();
while (length-- > 0) yield iterator.next().value;
}
// Example:
const set = new Set([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
console.log(...take(set, 3));
There is no built in method to take only a certain number of items from an iterable (ala something like take()). Although your snippet can be somewhat improved with a for of loop, which is specifically meant to work with iterables, eg:
let a = []; let i = 0; for (let x of Z()) { a.push(x); if (++i === N) break; }
Which might be better since your original snippet would continue looping even if there are not N items in the iterable.
A bit shorter and less efficient with .map, and a bit safer with custom function:
function *Z() { for (let i = 0; i < 5; ) yield i++; }
function buffer(t, n = -1, a = [], c) {
while (n-- && (c = t.next(), !c.done)) a.push(c.value); return a; }
const l = console.log, t = Z()
l( [...Array(3)].map(v => t.next().value) )
l( buffer(t) )
how can I achieve this more elegantly?
One possible elegant solution, using iter-ops library:
import {pipe, take} from 'iter-ops';
const i = pipe(
Z(), // your generator result
take(N) // take up to N values
); //=> Iterable<number>
const arr = [...i]; // your resulting array
P.S. I'm the author of the library.

JavaScript Remove Multiple Values from Array Using Filter and Loop

I'm new here and need some help with writing a function destroyer() to remove multiple values from an array.
The destroyer() function passes in an array and additional numbers as arguments. The idea is to remove the numbers from the array.
E.g.
destroyer([1, 2, 3, 1, 2, 3], 2, 3)
Output: [1, 1]
destroyer(["tree", "hamburger", 53], "tree", 53)
Output: ["hamburger"]
destroyer([2, 3, 2, 3], 2, 3)
Output: []
Note: the examples only show 2 additional numbers to remove. But the function destroyer() should be able to remove any number of values (i.e. 4, 5, or 6 parameters).
However, my code does not produce the same result. Specifically, using console.log, I see that my filterer function does not loop properly.
1) Can anyone help me debug?
2) Any better way to write this function?
Thank you very much!!!
function destroyer() {
var args = Array.prototype.slice.call(arguments);
var itemToRemove = args.slice(1);
console.log(itemToRemove);
var newArr = args[0];
console.log(newArr);
function filterer(value) {
for (var i = 0; i < itemToRemove.length; i++) {
console.log(i);
console.log(itemToRemove[i]);
if (value != itemToRemove[i]) {
return value;
}
}
}
return newArr.filter(filterer);
}
Your filterer function can be much simpler:
function filterer (value) {
return itemToRemove.indexOf(value) === -1;
}
Using Array.prototype.indexOf() can be inefficient compared to object property lookup in terms of time complexity. I would recommend looping through the additional arguments once and constructing an object with target elements to be destroyed as keys. Then you can check if a given value is a target or not within a filtering callback that you pass to Array.prototype.filter().
function destroyer() {
var arr = arguments.length && arguments[0] || [];
var targets = {};
for (var i = 1; i < arguments.length; i++) {
targets[arguments[i]] = true;
}
return arr.filter(function (x) {
return targets[x] === undefined;
});
}
One downside to this approach is that not all values in JS can be valid properties of an object, since properties must be strings. In this case, you're just using numbers as keys, and those numbers are implicitly converted to strings.
We can pass the arguments an extra parameter to our callback function in our filter() method.
function destroyer(arr) {
return arr.filter(filterer(arguments)); // Pass arguments
}
function filterer(args) {
return function(value) { // Actual filter function
for (var i = 1; i < args.length; i++) {
if (value === args[i]) // Seek
return false; // Destroy
}
return true; // Otherwise keep
};
}
This passes all 5 test cases for freeCodeCamp | Basic Algorithm Scripting | Seek and Destroy.
The following code will remove elements from an array. The elements it removes are defined by any extra parameters. ...remove is an ES6 feature that aggregates extra parameters into a single array.
I will iterate over the ...remove array and delete that element from the main array we are working on.
Here is a JSFiddle: https://jsfiddle.net/zzyopnnp/
...extra_parameters is not supported in most browsers, you may want to use the arguments object.
function removeIndex(array, index) {if(index>-1){array.splice(index, 1);}}
function destroyer(array, ...remove) {
remove.forEach(function(elem, index) {
removeIndex(array, index);
});
};
var arr = ["tree", "hamburger", 53];
destroyer(arr, "tree", 53);
console.log(arr);
A simple function
function filter(arr, arg1, arg2){
var j = arr.length;
while(j){
if (arr.indexOf(arg1) > -1 || arr.indexOf(arg2) > -1){
arr.splice(j - 1, 1);
}
j--
}
}
For multiple arguments
A simple function
function filter(){
var j = -1;
for(var i = 1; i < arguments.length; i++){
j = arguments[0].indexOf(arguments[i]);
if(j > -1){
arguments[0].splice(j, 1);
}
}
return arguments[0];
}
you can call this function with no of args
eg:
filter([1,2,3,4,5,6,7,8,9], 1, 3, 5); //return [2,4,6,7,8,9]
filter([1,2,3,4,5,6,7,8,9], 1); //return [2,3,4,5,6,7,8,9]
There are really good answers here, but you can do it very clean in this way, remember you have an objects option in filter method which you can use in the callback function, in this case i'm using it like : arguments[i] so I can check every value in the arguments array
function destroyer(arr) {
for(var i = 1; i < arguments.length; i++){
arr = arr.filter(isIn, arguments[i]);
}
function isIn(element,index, array){
if (element != this){
return element;
}
}
return arr;
}

Categories

Resources