I'm trying to play a bit and implementing a memoization pattern for multiple values using JavaScript.
I managed to write the code for single value:
var lazy = {};
lazy.memoization = evaluator => {
const cache = new Map;
return key => {
cache.has(key) || cache.set(key, evaluator(key));
return cache.get(key);
};
};
var memoize = lazy.memoization(key => console.log('computing ', key));
memoize(1); // -> computing 1
memoize(2); // -> computing 2
Here is the version for multiple keys and it does not work as expected, it just outputs 'computing Array []', 'computing undefined':
var lazy = {};
lazy.memoization = evaluator => {
const cash = new Map;
return (...keys) => {
var values = [];
keys.reduce((v, f) => {
if (!cash.has(v)) {
cash.set(v, evaluator(v));
}
values.push(cash.get(v));
}, values);
return values;
};
};
var memoizeMultiple = lazy.memoization((...keys) => {
keys.forEach(key => console.log('computing ', key))
});
memoizeMultiple(1, 2);
What is wrong here?
There are a bunch of problems with your code. First off, reduce is a kind of fold, which means it usually is used to "collapse" a data structure into a single value. In order to do this, the function passed into reduce gets the accumulation value first and each value inside the data structure second.
const sumOf = ns => ns.reduce((sum, num) => sum + num, 0);
sumOf([1, 2, 3, 4, 5]); // -> 15
In this example, the data structure is an Array, which holds Number values. reduce is used to collapse all numbers in the array into a final value (by summing them up). The collapsing function is called a reducer function (it does addition in this example). Finally, the 0 passed into reduce is the seed value.
Let's track down what happens step by step:
In the first iteration, the reducer function is passed the seed value and the first number in the array. It therefor looks like:
(0, 1) => 0 + 1
The second iteration starts with the result from the first iteration as the accumulator value and the second number in the array:
(1, 2) => 1 + 2
So in complete, it works like this:
(0, 1) => 0 + 1
(1, 2) => 1 + 2
(3, 3) => 3 + 3
(6, 4) => 6 + 4
(10, 5) => 10 + 5
After the last iteration, reduce returns the final accumulator, which is 15 in this example.
OK, back to the code you provided. Your "multi arguments memoization" version uses reduce, but the reducer function doesn't return the intermediate results as a new accumulator value and you don't return the final result reduce produces.
Another problem is your evaluator function. The value it returns is stored inside the caching Map instance. In your code, it doesn't return anything other than undefined. Therefor, undefined is stored and returned on subsequent calls to the memoized function.
If we address these issues, it works:
var lazy = {};
lazy.memoization = evaluator => {
const cache = new Map();
return (...args) => {
return args.reduce((acc, arg) => {
if (!cache.has(arg)) {
cache.set(arg, evaluator(arg)); // stores the returned value inside the cache variable
}
return acc.concat(cache.get(arg));
}, []); // the result should be an array, so use that as the seed
};
};
var memoizeMultiple = lazy.memoization(value => {
console.log('computing ', value)
return value; // you have to return something in here, because the return value is stored
});
memoizeMultiple(1, 2);
I hope this clarifies things a bit. Happy coding!
Related
I wrote a function that flatten and array.
const multiArray = [1, [2, 3, [4, [5]]], 6, [7, 8, 9, [10]]]
let newArray = []
const flatenArray = (params) => {
params.map((sm) => {
if(Array.isArray(sm)) {
return flatenArray(sm)
}
newArray.push(sm);
})
return newArray
}
//console.log(flatenArray(multiArray)) result [1,2,3,4,5,6,7,8,9,10]
The above function works fine.
but if I remove the return keyword from the "if" block then does it returns something else?
let newArray2 = []
const flatenArray2 = (params) => {
params.map((sm) => {
if(Array.isArray(sm)) {
flatenArray(sm)
}
newArray2.push(sm);
})
return newArray2
}
console.log(flatenArray2(multiArray))
//result [1,[2,3,[4,[5]]],6,[7,8,9,[10]]]
Now I would like to know...
Why should I use the return keyword there even when I'm calling it?
What is happening when I use the return keyword there?
What is happening when I use the return keyword there?
The function (the callback that you're passing to map) does return a value and stop executing. This means it will not reach the next line, newArray.push(sm);, after the if statement, which does push the sm array onto the result array in your second implementation.
Why should I use the return keyword there even when I'm calling it?
You shouldn't actually. Using an else statement would be much cleaner (and also you shouldn't abuse map):
const flattenArray = (params) => {
for (const sm of params) {
if (Array.isArray(sm)) {
flattenArray(sm)
} else {
newArray.push(sm);
}
}
return newArray
}
Every function has to "return" something, and that's result value of your function!
Imagine a sum function without a return value!
const noreturn_sum = (a, b) => {
a + b
}
if you don't return something, your function returns undefined implicitly.
so if you gonna print the result of noreturn_sum, you will get:
console.log(noreturn_sum(1, 2))
>> undefined
This is true even in your example, let me explain : (may I cleanup your code a little bit?)
let newArray2 = []
const flatenArray2 = (params) => {
// PART 1 ---------
params.map((sm) => {
if(Array.isArray(sm)) {
flatenArray(sm) // PART 1.map.fn ---------
}
newArray2.push(sm);
})
// END PART 1 ---------
// PART 2 ---------
return newArray2
// END PART 2 ---------
}
console.log(flatenArray2(multiArray))
A. your function for each parameter does this:
checks if it's an array:
true => call the function again to reach to a non-array value
false [it's a value] => add it to newArray2
then returns undefined
B. then your whole params.map(... returns an array of undefined, because your function inside map (that I explained it in (1.) and (2.) returns undefined
C. you return newArray2 at the end of the function.
note that your flatenArray2 function always return the newArray2 but inside "PART 1.map.fn" you "discard" it (or you "ignore" it in other words)
but in the last experssion console.log(flatenArray2(multiArray)) you gave the returned value to the console.log function.
When you do not use the result, either by assigning, passing or returning, you are solely relying on the side effects on the function.
Eg. in your flatenArray if you pass it a [[1]] sm will be [1], thus the if test is true and it will recurse with [1]. It will then fail on the test because 1 is not an array and push 1 to newArray. It then returns newArray which is then instantly returned and no more of that function is used in that round. You never use the resulting array from map since you are never ever returning this so one could question why you didn't use forEach instead to enforce that it is just for side effects (mutating newArray). If you were to remove return it will continue down and do newArray.push(sm) even though it is an array Thus you would get both flattened results and unflattened results in the result array mixed and doubled up. This could be prevented by having the last statement in an else so that the function either did the array thing or the element thing.
The flatenArray2 doesn't recurse as it uses flatenArray that populates newArray and not newArray2 so it will only make a shallow copy of the original array due to the flattened elements being added to newArray and the unflattened elements being added to newArray2 which is the only result that is returned.
If you were to call any function twice you will get the results of the previous runs in the second.
This is how I would have made it to prevent it from keeping results from previous rounds and actually using the results returned:
const multiArray = [1, [2, 3, [4, [5]]], 6, [7, 8, 9, [10]]];
const flattenPure = (arr) => {
const helper = (acc, e) =>
Array.isArray(e)
? e.reduce(helper, acc) // reduce the array e with acc as accumulator
: [...acc, e]; // make a new array with elements from acc and the element e
return arr.reduce(helper, []); // start with an empty accumulator
}
console.log("Once Pure", flattenPure(multiArray));
console.log("Twice Pure", flattenPure(multiArray));
Using Array.proptype.push makes a faster version, however then why not just use Array.prototype.flatten)?
const multiArray = [1, [2, 3, [4, [5]]], 6, [7, 8, 9, [10]]];
const flattenSide = (arr) => {
let res = []; // this is new every time flatten is called
const helper = (e) => {
if (Array.isArray(e)) {
e.forEach(helper); // indirect recurse for each element
} else {
res.push(e);
}
// hidden return undefined here
};
helper(arr); // flatten arr with result in res
return res; // return result
}
console.log("Once Side", flattenSide(multiArray));
console.log("Twice Side", flattenSide(multiArray));
I have an object that I need to convert into an array. This is the object:
const dogCounts: {
maltese: 4,
poodle: 2,
labrador: 10,
corso: 0
}
And I send it to a component thru props and I have a useMemo hook to convert it into a structure like this: [ [maltese, 4], [poodle, 2], ... ]
const formatDogCounts = useMemo(() => {
const z = Object.keys(dogCounts || {})?.map(i => {
if (dogCounts[i] === 0) return; // Don't add it to map and skip it
return [i, dogCounts[i]]
})
}, [dogCounts])
When the number is zero I don't want to add it to the formatDogCounts variable. What I put above doesn't fit to eslints rules. Arrow function expected no return value.eslintconsistent-return.
Also I put that {} in the object.keys for the case when the counts haven't loaded yet is there a cleaner way to null check that?
map doesn't filter out values; doing a simple return; in map makes the corresponding entry in the result array undefined. If you want to do that, you'll need to filter first, or build the array another way (such as a simple loop).
Here's the filter approach:
const formatDogCounts = useMemo(() => {
const z = Object.keys(dogCounts || {})?
.filter(name => dogCounts[name] !== 0)
.map(name => [name, dogCounts[i]]);
}, [dogCounts]);
Note that Object.entries provides the very [name, value] pairs you want, so you could avoid map, and there's no reason for the conditional chaining operator as neither Object.keys nor Object.entries ever returns undefined or null:
const formatDogCounts = useMemo(() => {
const z = Object.entries(dogCounts || {})
.filter(([, value]) => value !== 0);
}, [dogCounts]);
Note the , prior to value in the [, value] destructuring pattern so we're grabbing the second array entry (the value), not the first (the name).
We can also avoid the calls to Object.entries and filter entirely when there is no dogCounts:
const formatDogCounts = useMemo(() => {
const z = dogCounts
? Object.entries(dogCounts).filter(([, value]) => value !== 0)
: [];
}, [dogCounts]);
In a comment you've said:
The entries solution worked really well for me! Is there a way now to return an object instead of an array with total dog counts and then an array of items? Ex: formatDogCounts: { totalDogs: 30, items: [...] }
Sure. If there will only ever be a reasonable number of dogs (fewer than hundreds of thousands), I'd just do it as a separate operation at the end:
const formatDogCounts = useMemo(() => {
const items = dogCounts
? Object.entries(dogCounts).filter(([, value]) => value !== 0)
: [];
return {
totalDogs: items.reduce((sum, [, value]) => sum + value, 0),
items,
};
}, [dogCounts]);
(A straight sum is the only ad hoc use of reduce I'll do, and even then I don't much care for it.)
Or you could make your filter callback slightly impure and count them as you go:
const formatDogCounts = useMemo(() => {
let totalDogs = 0;
const items = dogCounts
? Object.entries(dogCounts).filter(([, value]) => {
totalDogs += value;
return value !== 0;
})
: [];
return {
totalDogs,
items,
};
}, [dogCounts]);
If you want to perform a map and filter operation together, you can use flatMap, returning an empty array to skip an element.
const formatDogCounts = useMemo(() => {
const z = Object.keys(dogCounts || {})?.flatMap(i => {
if(dogCounts[i] === 0) return []; // Dont add it to map and skip it
return [[i, dogCounts[i]]];
})
}, [dogCounts])
You are trying to filter using a map which isn't possible. Map will return the same amount of values as you put in. You can do a foreach or you can do a combination of map and filter to get the expected results.
Foreach
const z = []
Object.keys(dogCounts).forEach((key) => {
if(dogCounts[key]) {
// if the value is truthy push the structure to the array.
z.push([key, dogCounts[key]]);
}
}
Map/Filter
const z = Object.keys(dogCount)
.map((key) => [key, dogCount[key]) // map to restructure object.keys
.filter(([key, value]) => value); // filter to remove falsey values (0, null, undefined)
Things I have tried include following where values is array of values of which I want to find the sum.
I have also added necessary functions from rxjs as follows:
I get an error saying
typeError: Rx.Observable.from(...).sum is not a function
const { merge , interval ,from} = rxjs;
const { tap ,take ,sum } = rxjs.operators;
var sumSource = Rx.Observable.from(values).sum(function (x) {
return x;
});
var subscription = sumSource.subscribe(
function (x) {
console.log('Next: ' + x);
x.target.value = x;
},
function (err) {
console.log('Error: ' + err);
},
function () {
console.log('Completed');
}
);
Not much is available about sum on internet.Any inputs to fix and get the sum?
sum: regarding the official rxjs github repository they do not export/provide the sum operator anymore.
reduce operator reduce applies an accumulator function over the source Observable, and returns the accumulated result when the source completes.
scan operator scan applies an accumulator function over the source Observable and returns the accumulated result every emit.
setup
const values = [1,2,3,4,5];
const accumulator = (acc, curr) => acc + curr;
implement reduce
from(values).pipe(
reduce(accumulator, 0)
)
// Expected output: 15
implement scan
from(values).pipe(
scan(accumulator, 0)
)
// expected output: 1, 3, 6, 10, 15
I made a running stackblitz here.
Can someone explain how this is working.
Learning to use Array.reduce()
var votes = ['tacos','pizza','pizza','tacos','fries','ice cream','ice cream','pizza'];
var initialValue = {}
var reducer = function(tally, vote) {
if (!tally[vote]) {
tally[vote] = 1;
} else {
tally[vote] = tally[vote] + 1;
}
return tally;
}
var result = votes.reduce(reducer, initialValue)
The way how reduce works is pretty similar to map or filter. In this case, the reducer is responsible for reducing the array of objects into one object.
The reducer function iterates through all elements of your array. The function is called with two arguments, tally - result of reducing so far and vote - array element that is currently being processed.
If tally does not have a property named just like the element that is currently being processed/reduced, it adds such a key to the object and sets its value to one. Otherwise (key is present), it's incremented by one.
For more information go here
There's plenty of verbose explanations of reduce here and other places, but I think a simple visual demonstration might help
[1,2,3].reduce(function(a,b) { return a + b }, 0)
// (((0 + 1) + 2) + 3)
// => 6
You can see:
an input list of 3 elements results in 3 computations
the initial value is used in the computation with the first element
the result of one computation is used in the next computation …
… up until the last computation which is the result of reduce
Now as for your code, we're not building a sum of numbers, we're building up an Object that holds the tally count of each string in an array.
There's no point in me explaining what everyone else did, but it's not surprising this might've been confusing for you because no one explained that this is abuse of an Object.
In truth, an Object is actually not the best data structure choice for this reduction. Here, an Object is used to emulate the behaviour of a Map.
We'll set initialValue to a new Map() and you'll see how the reducer uses more descriptive syntax within. Note comments aren't even necessary because the code says everything it needs to.
var votes = ['tacos','pizza','pizza','tacos','fries','ice cream','ice cream','pizza']
var initialValue = new Map()
var reducer = function (tally, vote) {
if (tally.has(vote))
return tally.set(vote, tally.get(vote) + 1)
else
return tally.set(vote, 1)
}
var result = votes.reduce(reducer, initialValue)
console.log(Array.from(result.entries()))
Output
[
[ "tacos", 2 ],
[ "pizza", 3 ],
[ "fries", 1 ],
[ "ice cream", 2 ]
]
just for funs
I can show you that the visual representation of the sum via reduce is correct/accurate by writing a reducer that builds a string instead of actually adding the numbers
var initialValue = 0
var reducer = function(a,b) { return '(' + a + ' + ' + b + ')' }
var result = [1,2,3].reduce(reducer, initialValue)
console.log(result)
// (((0 + 1) + 2) + 3)
This technique might be useful for you when trying to understand/debug your own reducers
Basically Array.prototype.reduce() method applies a function against an accumulator and each value of the array to reduce it to a single value.
In your example the reduced value (the result of a count operation) is assigned as a property of an object called tally which is returned by .reduce();
I comment your with a brief explanation:
// your data in an array
var votes = ['tacos','pizza','pizza','tacos','fries','ice cream','ice cream','pizza'];
// optional value to use as the first argument to the first call of the callback when using Array.prototype.reduce().
var initialValue = {}
// tally = previousValue and vote = currentValue
var reducer = function(tally, vote) {
// if tally is not assign as a key in tally object, add key and add value of one, (basically count 1 for one element in your votes array)
if (!tally[vote]) {
tally[vote] = 1;
} else {
// otherwise if tally object has already this key, increment its value by one, (basically it counts how many times each item in votes array is present in the array)
tally[vote] = tally[vote] + 1;
}
return tally;
}
var result = votes.reduce(reducer, initialValue);
console.log(result);
Notes: you can actually avoid using of declaring a variable for initialValue and instead using only var result = votes.reduce(reducer, {});
API documentation:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce
Some more examples and brief explanation:
https://www.airpair.com/javascript/javascript-array-reduce
I inserted some console.log messages in your code and this helped me understand what was going on.
var votes = ['tacos', 'pizza', 'pizza', 'tacos', 'fries', 'ice cream', 'ice cream', 'pizza'];
var initialValue = {}
var reducer = function(tally, vote) {
console.log("tally: ", tally);
console.log("vote: ", vote);
console.log("tally[vote]: ", tally[vote]);
if (!tally[vote]) {
tally[vote] = 1;
} else {
tally[vote] = tally[vote] + 1;
}
return tally;
}
var result = votes.reduce(reducer, initialValue)
console.log("result: " + JSON.stringify(result));
The reduce() method reduces the array to a single value.
The reduce() method executes a provided function for each value of the array (from left-to-right).
The return value of the function is stored in an accumulator (result/total).
You have this given list:
var votes = ['tacos','pizza','pizza','tacos','fries','ice cream','ice cream','pizza'];
Then you make an Initial List. You can modify this and the function will be applied for both lists.
var initialValue = {}
"reducer" is initialized as a function. The reason that we place the function in "reducer" is so we can call it more easily further on. You can do whatever you want in this function and it will be executed on the list in the upcoming steps
var reducer = function(tally, vote) {
if (!tally[vote]) {
tally[vote] = 1;
} else {
tally[vote] = tally[vote] + 1;
}
return tally;
}
Finally, the result of the function is stored into the variable.
var result = votes.reduce(reducer, initialValue)
Well, let's try to expand the iteration:
When you execute
votes.reduce(reducer, initialValue)
It actually does this:
reducer(initialValue, votes[0]); // step1, return {'tacos': 1}
reducer(returnedValueOfStep1, votes[1]); // step2, return {'tacos': 1, 'pizza': 1}
reducer(returnedValueOfStep2, votes[2]); // step3, return {'tacos': 1, 'pizza': 2}
reducer(returnedValueOfStep3, votes[3]); // step4 ...
reducer(returnedValueOfStep4, votes[4]); // step5 ...
reducer(returnedValueOfStep5, votes[5]); // step6 ...
reducer(returnedValueOfStep6, votes[6]); // step7 ...
reducer(returnedValueOfStep7, votes[7]); // step8 ...
I think it quite clear right now.
And usually, if we do not provide initialValue as the second parameter for reduce, it will take the first element of array as the initialValue, and starts iteration from the second one:
reducer(votes[0], votes[1]); // step1 ...
reducer(returnedValueOfStep1, votes[2]); // step2 ...
...
I would like to filter an array of items by using the map() function. Here is a code snippet:
var filteredItems = items.map(function(item)
{
if( ...some condition... )
{
return item;
}
});
The problem is that filtered out items still uses space in the array and I would like to completely wipe them out.
Any idea?
EDIT: Thanks, I forgot about filter(), what I wanted is actually a filter() then a map().
EDIT2: Thanks for pointing that map() and filter() are not implemented in all browsers, although my specific code was not intended to run in a browser.
You should use the filter method rather than map unless you want to mutate the items in the array, in addition to filtering.
eg.
var filteredItems = items.filter(function(item)
{
return ...some condition...;
});
[Edit: Of course you could always do sourceArray.filter(...).map(...) to both filter and mutate]
Inspired by writing this answer, I ended up later expanding and writing a blog post going over this in careful detail. I recommend checking that out if you want to develop a deeper understanding of how to think about this problem--I try to explain it piece by piece, and also give a JSperf comparison at the end, going over speed considerations.
That said, **The tl;dr is this:
To accomplish what you're asking for (filtering and mapping within one function call), you would use Array.reduce()**.
However, the more readable and (less importantly) usually significantly faster2 approach is to just use filter and map chained together:
[1,2,3].filter(num => num > 2).map(num => num * 2)
What follows is a description of how Array.reduce() works, and how it can be used to accomplish filter and map in one iteration. Again, if this is too condensed, I highly recommend seeing the blog post linked above, which is a much more friendly intro with clear examples and progression.
You give reduce an argument that is a (usually anonymous) function.
That anonymous function takes two parameters--one (like the anonymous functions passed in to map/filter/forEach) is the iteratee to be operated on. There is another argument for the anonymous function passed to reduce, however, that those functions do not accept, and that is the value that will be passed along between function calls, often referred to as the memo.
Note that while Array.filter() takes only one argument (a function), Array.reduce() also takes an important (though optional) second argument: an initial value for 'memo' that will be passed into that anonymous function as its first argument, and subsequently can be mutated and passed along between function calls. (If it is not supplied, then 'memo' in the first anonymous function call will by default be the first iteratee, and the 'iteratee' argument will actually be the second value in the array)
In our case, we'll pass in an empty array to start, and then choose whether to inject our iteratee into our array or not based on our function--this is the filtering process.
Finally, we'll return our 'array in progress' on each anonymous function call, and reduce will take that return value and pass it as an argument (called memo) to its next function call.
This allows filter and map to happen in one iteration, cutting down our number of required iterations in half--just doing twice as much work each iteration, though, so nothing is really saved other than function calls, which are not so expensive in javascript.
For a more complete explanation, refer to MDN docs (or to my post referenced at the beginning of this answer).
Basic example of a Reduce call:
let array = [1,2,3];
const initialMemo = [];
array = array.reduce((memo, iteratee) => {
// if condition is our filter
if (iteratee > 1) {
// what happens inside the filter is the map
memo.push(iteratee * 2);
}
// this return value will be passed in as the 'memo' argument
// to the next call of this function, and this function will have
// every element passed into it at some point.
return memo;
}, initialMemo)
console.log(array) // [4,6], equivalent to [(2 * 2), (3 * 2)]
more succinct version:
[1,2,3].reduce((memo, value) => value > 1 ? memo.concat(value * 2) : memo, [])
Notice that the first iteratee was not greater than one, and so was filtered. Also note the initialMemo, named just to make its existence clear and draw attention to it. Once again, it is passed in as 'memo' to the first anonymous function call, and then the returned value of the anonymous function is passed in as the 'memo' argument to the next function.
Another example of the classic use case for memo would be returning the smallest or largest number in an array. Example:
[7,4,1,99,57,2,1,100].reduce((memo, val) => memo > val ? memo : val)
// ^this would return the largest number in the list.
An example of how to write your own reduce function (this often helps understanding functions like these, I find):
test_arr = [];
// we accept an anonymous function, and an optional 'initial memo' value.
test_arr.my_reducer = function(reduceFunc, initialMemo) {
// if we did not pass in a second argument, then our first memo value
// will be whatever is in index zero. (Otherwise, it will
// be that second argument.)
const initialMemoIsIndexZero = arguments.length < 2;
// here we use that logic to set the memo value accordingly.
let memo = initialMemoIsIndexZero ? this[0] : initialMemo;
// here we use that same boolean to decide whether the first
// value we pass in as iteratee is either the first or second
// element
const initialIteratee = initialMemoIsIndexZero ? 1 : 0;
for (var i = initialIteratee; i < this.length; i++) {
// memo is either the argument passed in above, or the
// first item in the list. initialIteratee is either the
// first item in the list, or the second item in the list.
memo = reduceFunc(memo, this[i]);
// or, more technically complete, give access to base array
// and index to the reducer as well:
// memo = reduceFunc(memo, this[i], i, this);
}
// after we've compressed the array into a single value,
// we return it.
return memo;
}
The real implementation allows access to things like the index, for example, but I hope this helps you get an uncomplicated feel for the gist of it.
That's not what map does. You really want Array.filter. Or if you really want to remove the elements from the original list, you're going to need to do it imperatively with a for loop.
Array Filter method
var arr = [1, 2, 3]
// ES5 syntax
arr = arr.filter(function(item){ return item != 3 })
// ES2015 syntax
arr = arr.filter(item => item != 3)
console.log( arr )
You must note however that the Array.filter is not supported in all browser so, you must to prototyped:
//This prototype is provided by the Mozilla foundation and
//is distributed under the MIT license.
//http://www.ibiblio.org/pub/Linux/LICENSES/mit.license
if (!Array.prototype.filter)
{
Array.prototype.filter = function(fun /*, thisp*/)
{
var len = this.length;
if (typeof fun != "function")
throw new TypeError();
var res = new Array();
var thisp = arguments[1];
for (var i = 0; i < len; i++)
{
if (i in this)
{
var val = this[i]; // in case fun mutates this
if (fun.call(thisp, val, i, this))
res.push(val);
}
}
return res;
};
}
And doing so, you can prototype any method you may need.
TLDR: Use map (returning undefined when needed) and then filter.
First, I believe that a map + filter function is useful since you don't want to repeat a computation in both. Swift originally called this function flatMap but then renamed it to compactMap.
For example, if we don't have a compactMap function, we might end up with computation defined twice:
let array = [1, 2, 3, 4, 5, 6, 7, 8];
let mapped = array
.filter(x => {
let computation = x / 2 + 1;
let isIncluded = computation % 2 === 0;
return isIncluded;
})
.map(x => {
let computation = x / 2 + 1;
return `${x} is included because ${computation} is even`
})
// Output: [2 is included because 2 is even, 6 is included because 4 is even]
Thus compactMap would be useful to reduce duplicate code.
A really simple way to do something similar to compactMap is to:
Map on real values or undefined.
Filter out all the undefined values.
This of course relies on you never needing to return undefined values as part of your original map function.
Example:
let array = [1, 2, 3, 4, 5, 6, 7, 8];
let mapped = array
.map(x => {
let computation = x / 2 + 1;
let isIncluded = computation % 2 === 0;
if (isIncluded) {
return `${x} is included because ${computation} is even`
} else {
return undefined
}
})
.filter(x => typeof x !== "undefined")
I just wrote array intersection that correctly handles also duplicates
https://gist.github.com/gkucmierz/8ee04544fa842411f7553ef66ac2fcf0
// array intersection that correctly handles also duplicates
const intersection = (a1, a2) => {
const cnt = new Map();
a2.map(el => cnt[el] = el in cnt ? cnt[el] + 1 : 1);
return a1.filter(el => el in cnt && 0 < cnt[el]--);
};
const l = console.log;
l(intersection('1234'.split``, '3456'.split``)); // [ '3', '4' ]
l(intersection('12344'.split``, '3456'.split``)); // [ '3', '4' ]
l(intersection('1234'.split``, '33456'.split``)); // [ '3', '4' ]
l(intersection('12334'.split``, '33456'.split``)); // [ '3', '3', '4' ]
First you can use map and with chaining you can use filter
state.map(item => {
if(item.id === action.item.id){
return {
id : action.item.id,
name : item.name,
price: item.price,
quantity : item.quantity-1
}
}else{
return item;
}
}).filter(item => {
if(item.quantity <= 0){
return false;
}else{
return true;
}
});
following statement cleans object using map function.
var arraytoclean = [{v:65, toberemoved:"gronf"}, {v:12, toberemoved:null}, {v:4}];
arraytoclean.map((x,i)=>x.toberemoved=undefined);
console.dir(arraytoclean);