(I couldn't find an open Lodash Slack channel, that's why I'm posting here.)
Could you please tell me why the partialRight in this fiddle seems to do nothing? The correctRenameKeys function correctly renames the key in the supplied object, but the wrongRenameKeys function - which should do exactly the same - doesn't.
Please open the JavaScript console in your browser to see logs when running the fiddle. I tested it in Chrome.
const renameKeysOfOneObject = (object, keyMappings) => {
return _.reduce(object, function(result, value, key) {
key = keyMappings[key] || key;
result[key] = value;
return result;
}, {});
};
const correctRenameKeys = (objects, keyMappings) => {
const keysRenamer = object => renameKeysOfOneObject(object, keyMappings);
return _.map(objects, keysRenamer);
};
const wrongRenameKeys = (objects, keyMappings) => {
const keysRenamer = _.partialRight(renameKeysOfOneObject, keyMappings);
return _.map(objects, keysRenamer);
};
const object = {keyToBeRenamed: 'someValue'};
const objects = [object];
const keyMapping = {keyToBeRenamed: 'newKeyName'};
const correctlyRenamed = correctRenameKeys(objects, keyMapping);
const wronglyRenamed = wrongRenameKeys(objects, keyMapping);
console.assert(_.isEqual(correctlyRenamed, wronglyRenamed),
"The two objects should be equal. " +
"The 'keyToBeRenamed' key should have been renamed to 'newKeyName'.");
console.log(correctlyRenamed);
console.log(wronglyRenamed);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>
There are two supporting statements from the lodash documentation that can answer your question:
lodash#partialRight
This method is like _.partial except that partially applied arguments
are appended to the arguments it receives.
lodash#map
Creates an array of values by running each element in collection thru
iteratee. The iteratee is invoked with three arguments: (value,
index|key, collection).
Notice that there are three arguments passed in a lodash#map iteratee, and since keyRenamer is passed as it's iteratee, then we can conclude that the invocation signature would look like this:
keyRenamer(value, index, collection, keyMappings);
If you really want to achieve the effect of having the second argument of the function renameKeysOfOneObject to be partially applied then use lodash#partial.
const wrongRenameKeys = (objects, keyMappings) => {
const keysRenamer = _.partial(renameKeysOfOneObject, _, keyMappings);
return _.map(objects, keysRenamer);
};
const renameKeysOfOneObject = (object, keyMappings) => {
return _.reduce(object, function(result, value, key) {
key = keyMappings[key] || key;
result[key] = value;
return result;
}, {});
};
const correctRenameKeys = (objects, keyMappings) => {
const keysRenamer = object => renameKeysOfOneObject(object, keyMappings);
return _.map(objects, keysRenamer);
};
const wrongRenameKeys = (objects, keyMappings) => {
const keysRenamer = _.partial(renameKeysOfOneObject, _, keyMappings);
return _.map(objects, keysRenamer);
};
const object = {keyToBeRenamed: 'someValue'};
const objects = [object];
const keyMapping = {keyToBeRenamed: 'newKeyName'};
const correctlyRenamed = correctRenameKeys(objects, keyMapping);
const wronglyRenamed = wrongRenameKeys(objects, keyMapping);
console.assert(_.isEqual(correctlyRenamed, wronglyRenamed),
"The two objects should be equal. " +
"The 'keyToBeRenamed' key should have been renamed to 'newKeyName'.");
console.log(correctlyRenamed);
console.log(wronglyRenamed);
.as-console-wrapper{min-height:100%;top:0}
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>
Related
I have this function in JS
function getMap(objectList) {
const objectMap = new Map();
IDS.foreach(id => {
const attribute = objectList.find(object => object.getId() === id);
if (attribute) {
objectMap.set(id, attribute);
} else {
objectMap.set(id, null);
}
}
This is a nested loop because of the find inside the for loop. How could this be simplified? If the nested loop cannot be simplified, can other parts be simplified?
Assuming object IDs are unique, it looks like all you really have to do is call getId on each object beforehand. The conditional operator may be used instead of if/else if you wish.
function getMap(objectList) {
const objectsById = new Map(
objectList.map(object => [object.getId(), object])
);
const objectMap = new Map();
for (const id of IDS) {
objectMap.set(id, objectsById.get(id) || null);
}
}
You could create an array with null entries for each ID, followed by entries for which you actually have values in objectList, and pass that array to the Map constructor:
function getMap(objectList) {
return new Map([
...IDs.map(id => [id, null]),
...objectList.map(object => [object.getId(), object])
]);
}
Using native code with a simple callback
const result = (IDS || []).map(function(id, idx, arr) {
const pos = (objectList || []).findIndex(object => object.getId() === id);
const output = [];
output[id] = (pos >= 0 ? objectList[pos] : null);
return output;
});
Hope this helps... ;D
Let's say I have input object like this one:
const obj = {
deep: {
someKey: 'someValue1!',
veryDeep: {
someOtherKey: 'someOtherValue'
}
},
deep2: {
aa: 'bba'
}
}
I want to write a function that will take this object as first argument and key string as second argument. Then it's gonna loop through obj with recursion and find the value and return it. So if I name this function getObjectValueOfKey it will be called like this: getObjectValueOfKey(obj, 'aa') and it will return 'bba'.
I think I'm close with this code:
const getObjectValueOfKey = (obj, key) => {
const keys = Object.keys(obj)
for (let currKey of keys) {
if (typeof obj[currKey] === 'object') {
getObjectValueOfKey(obj[currKey], key)
}
if (currKey === key) {
return obj[key]
}
}
}
const res = getObjectValueOfKey(obj, 'aa')
console.log('res', res)
but for some unknown to me reasons res is undefined.
you were pretty close, the recursion was good but the problem is related with what all the paths in your code doesn't return, actually the only path that return is when you found the key in the first level of the object, after you made the recursion and found the result but nothing was returned, the code reach the end the of function and javascript return by default undefined if any return is not found, to solve the problem a decide to store the results found in the recursion in a variable and return that value at the end of the function.
const getObjectValueOfKey = (obj, key, found) => {
const keys = Object.keys(obj);
let result = undefined;
result ||= found;
for (let currKey of keys) {
if (typeof obj[currKey] === "object") {
result ||= getObjectValueOfKey(obj[currKey], key, result);
}
if (currKey === key) {
return obj[key];
}
}
return result;
};
const res = getObjectValueOfKey(obj, "aa", undefined);
console.log("res", res);
Imagine I have a data model like so:
// Pseudo-code, as I don't know how to achieve this
const Model = {
name: String.required,
description: String.optional,
rank: Number.required,
// ...
}
Basically, that means the Object must have a property name and it has to be a String. The property description is optional, but has to be a String if defined. Etc.
Now, let's say I have a bunch of Objects :
const obj1 = {
name: 'Object 1',
rank: 1,
}
const obj2 = {
description: 'This is object 2',
rank: 2,
}
In order to save my objects in a NoSQL database (Firebase's Firestore), I'd like to be sure that each Object matches the Model. This would prevent having missing fields for instance.
In my example, obj1 should pass but obj2 shouldn't has the propery name is missing.
And is there a way to throw in the error message which properties are missing?
Solution:
You can create a simple function that will validate your models based on an Array of required and optional parameters.
Note: The below has required and optional parameters hardcoded within the function, but you can also pass them in with very little effort and change in code. Simply remove the declarations and add them as parameters to the function.
Code
function validateModel(obj) {
let
// parameters specification
required = ["name", "rank"],
optional = ["description"],
// helper methods
toObjects = (a, v) => Reflect.has(obj, v) ? (a[v] = obj[v], a) : a,
grab = (type) => type.reduce(toObjects, {}),
payload = () => Object.assign(grab(required), grab(optional)),
notValid = v => !Reflect.has(obj, v);
//output
if (required.some(notValid)) return false;
return payload();
}
Working Example:
//setup
let log = (x) => { return console.log(x), log };
const Model={name:"n",description:"d",rank:"r"},Model2={rank:"a"},Model3={rank:"b",name:"b"};
// Validation Method:
function validateModel(obj) {
let
// parameters specification
required = ["name", "rank"],
optional = ["description"],
// helper methods
toObjects = (a, v) => Reflect.has(obj, v) ? (a[v] = obj[v], a) : a,
grab = (type) => type.reduce(toObjects, {}),
payload = () => Object.assign(grab(required), grab(optional)),
notValid = v => !Reflect.has(obj, v);
//output
if (required.some(notValid)) return false;
return payload();
}
// Examples:
log
(validateModel(Model) || "Invalid Model")
(validateModel(Model2) || "Invalid Model")
(validateModel(Model3) || "Invalid Model")
Making it Multi-Purpose:
If you create something like a model checker, it would be best to be able to check multiple types of Models with it. The below code creates a constructor that takes an Object full of Structs. Each Struct contains an array of required and optional parameters.
The ModelChecker constructor returns an object with a single method, validate. By passing in the object you want to check and the name of the model structure you want to check against, you can determine if a Model is valid.
Working Example:
//setup
let log = (x) => { return console.log(x), log };
const Model={name:"n",description:"d",rank:"r"},Model2={rank:"a"},Model3={rank:"b",name:"b"}, Car={"color":"black", "name": "toyota" }, Car2={"name":"chevy"};
//setup validation structures
const structs = {
"identity": {
required: ["name", "rank"],
optional: ["description"]
},
"car": {
required: ["color"],
optional: ["name"]
}
}
// model checker constructor
function ModelChecker(structs) {
let proto = {
structs
};
proto.validate = function validateModel(obj, struct) {
if (!this.structs[struct]) return false;
let
// parameters specification
required = this.structs[struct].required,
optional = this.structs[struct].optional,
// helper methods
toObjects = (a, v) => Reflect.has(obj, v) ? (a[v] = obj[v], a) : a,
grab = (type) => type.reduce(toObjects, {}),
payload = () => Object.assign(grab(required), grab(optional)),
notValid = v => !Reflect.has(obj, v);
//output
if (required.some(notValid)) return false;
return payload();
}
return proto;
}
// Examples:
let mc = ModelChecker(structs);
log
(mc.validate(Model, "identity") || "Invalid Model")
(mc.validate(Model2, "identity") || "Invalid Model")
(mc.validate(Model3, "identity") || "Invalid Model")
(mc.validate(Car, "car") || "Invalid Model")
(mc.validate(Car2, "car") || "Invalid Model")
const f = (arg1) => (arg2) => { /* returns something */ }
Is it possible to memoize f with regard to the 2 arguments, namely:
f(1)(2);
f(1)(3); // Cache not hit
f(4)(2); // Cache not hit
f(1)(2); // Cache hit
You could take a Map as cache and take nested maps for all following arguments.
This cache works for arbitrary count of arguments and reuses the values from the former calls.
It works by taking a curried function and an optional Map. If the map is not supplied, a new map is created which serves as base cache for all other calls of the returned closure or the final result.
The inner function takes a single argument and checks if this value is in the map.
If not, call the curried function and check the returned value
if function, create a new closure over the function and a new map,
if no function take the result,
as value for a new element of the map.
Finally return the value from the map.
const cached = (fn, map = new Map()) => arg => {
const inCache = map.has(arg);
const hint = inCache ? 'in cache' : 'not in cache';
console.log(arg, hint);
if (!inCache) {
const value = fn(arg);
const result = typeof value === 'function' ? cached(value, new Map()) : value;
map.set(arg, result);
}
return map.get(arg);
};
const f = a => b => c => a * b * c; // the original curried function
const g = cached(f); // its cached variant
console.log(g(1)(2)(5)); // not not not 10
console.log(g(1)(3)(4)); // in not not 12
console.log(g(4)(2)(3)); // not not not 24
console.log(g(1)(2)(6)); // in in not 12
console.log(g(4)(2)(3)); // in in in 24
.as-console-wrapper { max-height: 100% !important; top: 0; }
Interesting question — you could have independent caches for each function. The cache on the outside function will hold functions. Each inside function could get its own independent cache. So calling f(10)(1) followed by f(10)(2) would result in calling a cached version of the inside function. Calling f(10)(1) again would hit both caches:
function getCachedF() {
// outer cache holds functions keyed to argument
let outer_memo = {}
const f = (arg1) => {
if (!outer_memo.hasOwnProperty(arg1)) {
// Create inner function on outer cache
// each inner function needs its own cache
// because it will return different values
// given different outer function calls
let inner_memo = {}
console.log("outer cache miss")
outer_memo[arg1] = (arg2) => {
// just a normal memoized function
// cache is simple key:value pair
if (!inner_memo.hasOwnProperty(arg2)) {
console.log("inner cache miss")
inner_memo[arg2] = arg1 + arg2
}
return inner_memo[arg2]
}
}
return outer_memo[arg1]
}
return f
}
let f = getCachedF()
// both caches miss
console.log("3+5", f(3)(5))
// cached result
console.log("3+5", f(3)(5))
// only inside cache hit
console.log("3+8", f(3)(8))
// inside cache only hits if both args are the same
console.log("10+8", f(10)(8))
Another alternative would be to have single cache with keys that are a combination of both arguments, but then the inner function would always have to be called.
This probably isn't the canonical memoization function.
A function that needs to cache its result is given a cache function that is used to store and retrieve previous results:
const sum = memo(cache => a => b => cache(`${a}+${b}`, () => a + b));
// ^^^^^ ^^^^^^^^^^^ ^^^^^^^^^^^
// A B C
A — The cache function is provided by the memo function.(A memoized function can opt-out from caching some results if necessary.)
B — A unique key for the result. (e.g. cache['1+2'] = 3)
C — A thunk that returns the result.(So we can check if we have it already before computing it.)
This supports both curried and non-curried functions but also functions that return a function as a value.
The memo function can be implemented as follow:
const memo = fn => {
const ns = Symbol();
const cache = (key, thunk) => cache[ns][key] ??= thunk();
cache[ns] = {};
return fn(cache);
};
I quite like the logical nullish assignment operator for managing the cache:
a ??= answer()
The expression on the right is evaluated and assigned to a if and only if a is not already defined. Then it returns the value of a:
const answer = () => (console.log('returning the answer'), 42);
let a;
a ??= answer();
//=> LOG: returning the answer
//=> 42
a ??= answer();
//=> 42
a ??= 40;
//=> 42
I've used a symbol to hide the actual cache set on the cache function. A symbol is not returned when enumerating properties of an object:
const foo = {};
const key1 = Symbol();
const key2 = 'bar';
foo[key1] = 42;
foo[key2] = 41;
Object.keys(foo);
//=> ['bar']
Object.entries(foo);
//=> [['bar', 41]]
Demo
// curried memoized function
const sum = memo(cache => a => b =>
cache(`${a}+${b}`,
() => (console.log(`computing ${a}+${b}…`), a+b)));
console.log(sum(1)(2));
console.log(sum(1)(2));
console.log(sum(1)(2));
// non-curried memoized function
const mul = memo(cache => (a, b) =>
cache(`${a}*${b}`,
() => (console.log(`computing ${a}*${b}…`), a*b)));
console.log(mul(2, 3));
console.log(mul(2, 3));
console.log(mul(2, 3));
// function-returning function
const deferred_sum = memo(cache => a => b =>
cache(`${a}+${b}`,
() => (console.log(`defer computing ${a}+${b}…`), () => a+b)));
console.log(deferred_sum(1)(2)());
console.log(deferred_sum(1)(2)());
console.log(deferred_sum(1)(2)());
<script>
const memo = fn => {
const ns = Symbol();
const cache = (key, thunk) => cache[ns][key] ??= thunk();
cache[ns] = {};
return fn(cache);
};
</script>
You can not to pass map to every function.
You can do like the next:
const memoize = fn => {
const cache = {};
return (...args) => {
const curriedFn = fn(...args);
return (...next) => {
const key = // generate your key
if (key in cache) return cache[key];
return (cache[key] = curriedFn(...next));
}
}
}
I'm wondering if there's a concise or specific way to access values in the middle of an FP chain in JavaScript. Example:
const somestuff = [true, true, false];
let filteredCount = 0;
somestuff.filter((val) => val)
.forEach((val) => console.log(val));
Above, I'd like to set filteredCount to the length of the array returned by the filter function. The most straight-forward way is:
const somestuff = [true, true, false];
const filteredStuff = somestuff.filter((val) => val);
let filteredCount = filteredStuff.length;
filteredStuff.forEach((val) => console.log(val));
This is certainly valid but it breaks our FP chain and introduces an additional holding variable. I'm wondering if there's a convention for accessing values in the middle of the chain. Something like .once() that runs once and implicitly returns the value passed in, but nothing like that exists.
For debugging, I often use a function called tap to temporarily add a side-effect (like your console.log) to a function:
const tap = f => x => (f(x), x);
This function returns whatever it is passed, but not before calling another function with the value. For example:
const tap = f => x => (f(x), x);
const tapLog = tap(console.log);
const x = tapLog(10);
console.log("x is", x);
Your snippet basically does this:
Filter a list
(log the list)
Retrieve a length property from an array
If you construct this function using pipe or compose, you can "inject" the console.log in between without interrupting the data flow:
const countTrues = pipe(
filter(isTrue),
prop("length")
);
const countTruesWithLog = pipe(
filter(isTrue),
tap(console.log),
prop("length")
);
In a snippet:
// Utils
const isTrue = x => x === true;
const prop = k => obj => obj[k];
const tap = f => x => (f(x), x);
const filter = f => xs => xs.filter(f);
const pipe = (...fns) => x => fns.reduce((res, f) => f(res), x);
// Logic:
// Filter an array using the isTrue function
// and return the length of the result
const countTrues = pipe(
filter(isTrue),
prop("length")
);
// Create a filter with a console.log side-effect
// and return the length of the result
const countTruesWithLog = pipe(
filter(isTrue),
tap(console.log),
prop("length")
);
// App:
const somestuff = [true, true, false];
console.log("pure:");
const countA = countTrues(somestuff)
console.log(countA);
console.log("with log:")
const countB = countTruesWithLog(somestuff);
console.log(countB);
The reason there's no Array.prototype method like that, is that it has a side effect. This is something that is specifically avoided in functional programming.
However if you don't care about writing 'Pure Functions', or even the functional paradigm, you could put the side effect in your callbacks, or write a function in the Array prototype.
ie.
Array.prototype.once = function(callback) {
callback(this)
return this
}
You also have other hacky options like in the other answer
I don't think there's something like that by default. What you can do is extend Array, but I'm not really fond of extending framework classes (clashes with other once implementations for example). In this case you'd end up with:
Array.prototype.once = function once(func) {
func(this);
return this;
}
which is called like:
var filteredStuff = somestuff
.filter((val) => val)
.once(function(array) {
console.log(array.length);
})
.forEach((val) => console.log(val));
On the other hand, you can try to use default functions. One of these function that can access all items at once is reduce. Define a function once, that will call its first parameter once (:)) and you'd end up with something like:
function once(func) {
return function(accumulator, currentValue, currentIndex, array) {
if(currentIndex === 1) {
func(array);
}
return array;
}
}
which you'd be able to call like this:
var filteredStuff = somestuff
.filter((val) => val)
.reduce(once(function(array) {
console.log(array.length);
}), [0])
.forEach((val) => console.log(val));
Notice the ugly [0] to ensure once calls the passed function at least once (empty array included).
Both solutions aren't too neat, but it's the best I can come up with given the criteria.