Trouble understanding memoize implementation - javascript

How does answerKey[parameters] work? if Array.prototype.slice.call(arguments)
returns an array [157, 687], is answerKey[parameters] storing an array as key?
function memoize(mathProblem) {
var answerKey = {};
return function(){
var parameters = Array.prototype.slice.call(arguments);
if (answerKey[parameters]) {
console.log('returning cached');
return answerKey[parameters];
} else {
answerKey[parameters] = mathProblem.apply(this, arguments);
return answerKey[parameters]
}
}
};
var multiply = function(a, b){
return a*b;
}
var memoMultiply = memoize(multiply);
console.log(memoMultiply(157, 687));
=>
107859
console.log(memoMultiply(157, 687))
=>
returning cached
107859

The square bracket notation will convert an array into a string
var answerKey = {};
var params = [157, 687];
answerKey[params] = 107859;
answerKey['157,687']; // 107859
So yes, the key is the content of the array as a string. This is not great practice.
EDIT REQUESTED
In general I try to avoid depending on strings that are created from Array.prototype.toString() because it has some odd behavior
for example. Nested arrays are flattened
[2, [3, 4], 5].toString(); // '2,3,4,5'
This is losing information about the source array and is indistinguishable from
[2, 3, 4, 5].toString();
To get around issues like these I suggest passing the array through JSON.stringify();
JSON.stringify([2, [3, 4], 5]); // '[2,[3,4],5]'
JSON.stringify([2, 3, 4, 5]); // '[2,3,4,5]'
This example will work with .toString(); but I think its a bad habit.

Related

Does JS support sorting with a key function, rather than a comparator?

JavaScript's array.sort method takes an optional compare function as argument, which takes two arguments and decides which one of them is smaller than the other.
However, sometimes it would be more convenient to customize the sort order with a key function, which is a function that takes one value as an argument and assigns it a sort key. For example:
function keyFunc(value){
return Math.abs(value);
}
myArr = [1, 3, -2];
myArr.sort(keyFunc);
// the result should be [1, -2, 3]
Does JavaScript have support for this, or is there no way around writing a full-blown comparison function?
There's no support for exactly what you describe, but it's quite trivial to write a standard .sort function that achieves the same thing, with minimal code - just return the difference between calling keyFunc on the two arguments to sort:
function keyFunc(value){
// complicated custom logic here, if desired
return Math.abs(value);
}
myArr = [1, 3, -2];
myArr.sort((a, b) => keyFunc(a) - keyFunc(b));
console.log(myArr);
// the result should be [1, -2, 3]
If the key function is complicated and you don't want to run it more than necessary, then it would be pretty simple to create a lookup table for each input, accessing the lookup table if keyFunc has been called with that value before:
const keyValues = new Map();
function keyFunc(value){
const previous = keyValues.get(value);
if (previous !== undefined) return previous
console.log('running expensive operations for ' + value);
// complicated custom logic here, if desired
const result = Math.abs(value);
keyValues.set(value, result);
return result;
}
myArr = [1, 3, -2];
myArr.sort((a, b) => keyFunc(a) - keyFunc(b));
console.log(myArr);
// the result should be [1, -2, 3]
As stated already you have to write that functionality yourself or extend the current array sort method etc.
Another approach is if you ware using lodash and its orderBy method ... then this becomes:
myArr=[1, 3, -2];
const result = _.orderBy(myArr, Math.abs)
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
You could use a closure over the wanted function.
const
keyFunc = value => Math.abs(value),
sortBy = fn => (a, b) => fn(a) - fn(b),
array = [1, 3, -2];
array.sort(sortBy(keyFunc));
console.log(array); // [1, -2, 3]
You can easily subtract the "keys" from the two elements:
myArr.sort((a, b) => keyFunc(a) - keyFunc(b));
You could also monkey patch sort:
{
const { sort } = Array.prototype;
Array.prototype.sort = function(sorter) {
if(sorter.length === 2) {
sort.call(this, sorter);
} else {
sort.call(this, (a, b) => sorter(a) - sorter(b));
}
};
}
So then:
myArr.sort(keyFunc);
works.

Combine all arrays in object into one array.

I'm developing an extension for Google chrome and I'd like to combine all arrays in a certain object into one array instead of them being split. So right now, my console o
chrome.storage.sync.get(null, function(all) {
// this returns everything in chrome's storage.
}
It looks something like this in my console:
However, I'd like it to actually have all the arraries combined into one, as such:
Object
feed_0: Array[364]
I've tried this:
chrome.storage.sync.get(null, function(all) {
var test = {}; test = all;
delete test['currently.settings'];
console.log(test);
var alpha = [];
var result = 0;
for(var prop in test) {
if (test.hasOwnProperty(prop)) {
var second = alpha.concat(prop);
console.log(second);
// or Object.prototype.hasOwnProperty.call(obj, prop)
result++;
}
}
});
But this returns this:
Here is how to get one array from the all object:
var test = Object.values(all).flat();
In older JavaScript versions, that do not support these functions, go for:
var test = Object.keys(all).reduce( (acc, a) => acc.concat(all[a]), [] );
To assign it to a feed_0 property, is of course not the difficulty:
test = { feed_0: test };
You can combine your feed_N arrays into one property using Object.keys and Array#reduce:
var data = {
feed_0: [0, 1, 2],
feed_1: [3, 4, 5],
feed_2: [6, 7, 8],
feed_3: [9, 10, 11]
}
data = Object.keys(data).reduce(function(result, k) {
[].push.apply(result.feed_0, data[k])
return result
}, { feed_0: [] })
console.log(data)

why are there different arguments in this javascript function

I saw this function , though it works fine but I am bit puzzled about the function expressions. Here is the code
mapForEach(arr, fn) {
var newArr = [];
for (var i = 0; i < arr.length; i++) {
newArr.push(fn(arr[i]))
}
return newArr;
}
can anybody explain to nme what this rather complicated code is actually doing?
Lets say you have var array = [1, 2, 3, 5]; and then run var array2 = mapForEach(array, function(i) { return i * 2; })
array2 would then contain [2, 4, 6, 10].
So it returns a new array where you have the ability to modify each record with a function
mapForEach enumerates an array and calls a supplied function on each element.
example:
var a = [1, 2, 3];
console.log(mapForEach(a, (x) => x * 2));
would create a new array with the values (and output to console):
[2, 4, 6]
Basically it is an implementation of javascript native array function map, which creates a new array with the results of calling a provided function on every element in this array.
More info about mentioned function you can find here https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map

How do I get this 'Sorted Union' function to work using Array.concat, in javaScript

I am going through this exercise on FCC which requires the following:
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.
This is my code:
function uniteUnique(){
var elm, exists = {},
outArr = [],
arr = [],
args = Array.prototype.slice.call(arguments);
args.forEach(function(arg) {
arr.concat(arg.filter(Boolean));
});
for(var i =0; i<arr.length; i++){
elm = arr[i];
if(!exists[elm]){
outArr.push(elm);
exists[elm] = true;
}
}
return arr;
}
My problem centers around this line.
args.forEach(function(arg) {
arr.concat(arg.filter(Boolean));
});
I'd like all the arguments/arrays to go through the filter method and then get concatenated, any help would be appreciated!
Boolean will not filter unique items, it will simply return Boolean(arg) value which is not the intended one.
Replace
args.forEach(function(arg) {
arr.concat(arg.filter(Boolean));
});
with
args.forEach(function(arg) {
arr.concat(arg.filter(function(val){
return arr.indexOf(val) == -1;
}));
});
This will only concatenate array items which are unique
Well may be you prefer the following single liner functional approach instead;
var getUniques = (...a) => a.reduce((p,c)=> p.concat(c)).reduce((p,c) => {!~p.indexOf(c) && p.push(c); return p},[]);
document.write("<pre>" + getUniques([1,2,3],[3,4,5],[3,4,5,6,7,8],[0,1,2,3,4,5,6,7,8,9]) + "</pre>");
The getUniques function returns an array of all uniques in the order of appearance. Note that there is no arguments object in the arrow functions but the ...rest parameters of ES6 work just as well for that purpose. Even if you don't like the functional approach the logic behind may influence you to implement the same functionality with conventional functions and for loops.
And an even more simplified version of the above one is as follows
var getUniques = (...a) => a.reduce((p,c) => {c.forEach(e => !~p.indexOf(e) && p.push(e)); return p});
document.write("<pre>" + getUniques([1,2,3],[3,4,5],[3,4,5,6,7,8],[0,1,2,3,4,5,6,7,8,9]) + "</pre>");
function union(arrays) {
const u = arrays.reduce(function(acc, iVal) {
return acc.concat(iVal);
})
return [...new Set(u)];
}
var arr1 = [5, 10, 15];
var arr2 = [15, 88, 1, 5, 7];
var arr3 = [100, 15, 10, 1, 5];
console.log(union([arr1, arr2, arr3])); // should log: [5, 10, 15, 88, 1, 7, 100]

Concatenate JSON arrays in a JSON objects in javascript

I have a JSON response like this:
{"result":[["abc","de"],["fgh"],["ij","kl"]]}
I want the response to be in the form:
{"result":["abc","de","fgh","ij","kl"]}
How can I achieve this?
From the mozilla docs
var flattened = [[0, 1], [2, 3], [4, 5]].reduce(function(a, b) {
return a.concat(b);
});
// flattened is [0, 1, 2, 3, 4, 5]
var test={"result":[["abc","de"],["fgh"],["ij","kl"]]};
var tmp=[];
for(var i in test.result){
for(var j in test.result[i]){
tmp.push(test.result[i][j]);
}
}
test.result=tmp;
alert(JSON.stringify(test));
jsfiddle link http://jsfiddle.net/fu26849m/
jsFiddle
var arrayToFlatten = [[0, 1], [2, 3], [4, 5]];
Native (from Merge/flatten an array of arrays in JavaScript?):
var flattenedNative = arrayToFlatten.reduce(function(a, b) {
return a.concat(b);
});
alert(flattenedNative); // 0,1,2,3,4,5
jQuery (from How to flatten array in jQuery?):
var flattenedJQuery = $.map(arrayToFlatten, function(n) {
return n;
});
alert(flattenedJQuery); // 0,1,2,3,4,5
Native alternative (from Merge/flatten an array of arrays in JavaScript?):
var flattenedNativeAlt = [].concat.apply([], arrayToFlatten);
alert(flattenedNativeAlt); // 0,1,2,3,4,5
My first suggestion for this is you should create json directly as you want to use.
Do not modify it after you get.
You can also use this , this will give you value as you want.:
var mainText= JSON.parse('{"result":[["abc","de"],["fgh"],["ij","kl"]]}');
var arr = [];
for(var val1 in mainText.result)
{
arr = arr.concat(mainText.result[val1]);
}
mainText.result = arr;
console.log(JSON.stringify(mainText));
The reduce() and concat() functions can be combined to flatten an array:
var json = {"result":[["abc","de"],["fgh"],["ij","kl"]]};
function concatArrays(a, b) { return a.concat(b); }
json.result = json.result.reduce(concatArrays);
console.log(json); //{"result":["abc","de","fgh","ij","kl"]}
See it in action:
http://jsfiddle.net/cazomufn/
I like lodash' flatten (if you can live with another dependency.)
json.result = _.flatten(json.result);
// { result:['abc','de','fgh','ij','kl'] }
For example reduce isn't supported before IE9 but lodash would still work (compatibility build).

Categories