Reputation: 1073
I've tried using rearg
with and without chaining, and I only get the error when using chaining. I've read up on the docs and it looks like rearg
ought to play nicely with chaining as it returns a chainable wrapper method. However, I get the following error:
Error: _.chain(...).keys(...).myFunc is not a function
with this code:
var myList = ['a','b','c','d']
var myJSON = {
'a':1,
'b':2
};
var myFunc = _.rearg(_.difference, [1, 0]); //switching order of arguments
var hasAllKeys = _.chain(myJSON)
.keys()
.myFunc(myList)
.value();
Of course this code works fine (though the output is not what I need):
var wrong = _.chain(myJSON)
.keys()
.difference(myList)
.values();
Upvotes: 2
Views: 599
Reputation: 8163
This answer expands on PhiLho's excellent answer. Hopefully this will help future readers to understand a little more about the problem and solutions. In the following code snippet, you can see the same results as in PhiLho's code snippet and also other 2 tests:
_.difference
instead of _.reverseDiff
: Which returns results when the object has some keys not found in keyList (instead of being the keyList the one that has keys not found in the object)._.difference(myList, _.keys(myJSON))
): which returns the expected output with less code (the intent of this question probably was not about this solution, but by showing this alternative you can probably understand the problem more easily).// Generic code to display resuts
var results = document.getElementById('pls-results');
function showHTML(html) {
results.insertAdjacentHTML('beforeend', html);
}
// Set up test data
var keyList = [ 'a', 'b', 'c', 'd' ];
var incompleteJson = { "a": 1, "b": 2 };
var fullJson = { "a": 1, "b": 2, "c": true, "d": "yes" };
var extraPropertyJson = { "a": 1, "b": 2, "c": true, "d": "yes", 'z': 26 };
// Set up test helper methods
var renderLine = function(label, obj) {
return "<tr><td>" + label + "</td><td>" + JSON.stringify(obj) + "</td></tr>";
};
var test = function(funcName, funcToTest) {
var html = "<h3>" + funcName + "</h3><table>";
html += renderLine("Incomplete Json", funcToTest(incompleteJson));
html += renderLine("Full Json", funcToTest(fullJson));
html += renderLine("Extra Property Json", funcToTest(extraPropertyJson));
html += "</table>";
showHTML(html);
};
// The real code
var local_ = _.runInContext();
local_.mixin({
'reverseDiff': _.rearg(_.difference, [1, 0]) // switching order of arguments
});
// Tests execution
test("_.difference", function(json) { return _.chain(json).keys(). difference(keyList).value(); });
test("local_.reverseDiff", function(json) { return local_.chain(json).keys().reverseDiff(keyList).value(); });
test("_.difference(keyList, _.keys(json))", function(json) { return _.difference(keyList, _.keys(json)); });
<div id="pls-results"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.10.1/lodash.js"></script>
Upvotes: 1
Reputation: 41132
I know the question is old, but it is one of the rare SO questions about chaining in Lodash remaining unanswered, and trying to answer it allows me to dig deeper in the documentation and in understanding this library... And it is a good opportunity to try the "Run JS code" feature of Stack Overflow...
I finally understood that it cannot work as you did.
First, is rearg
really chainable? The answer is: yes.
I tried with this code:
var last = _(twoParams).ary(1).rearg([1, 0]).value();
and indeed, when I call last
with two parameters, it calls twoParams
with only the last one.
Of course, rearg
can be only chained with functions providing a function as output, and expecting a function as input, ie. only in a chain of functions processing functions.
Now, your code doesn't work because if rearg
is chainable, its output isn't!
Its output is a plain old JavaScript function, and these are not chainable.
The good news is that Lodash, in its great wisdom, provides a way to make a function to be chainable.
You have to use mixin
which, by default, adds the provided function to Lodash and makes it chainable.
See, for example, Create chain in lodash with custom functions which explains how to do it.
Unlike these examples, I will follow the advice of the Lodash documentation, and use runInContext
to avoid polluting Lodash with a new function that should remain local.
Here are my experiments. I took the liberty to rename some identifiers, as I hate the myXxx names and prefer more descriptive names...
// Generic code to display resuts
var results = document.getElementById('pls-results');
function showHTML(html)
{
results.insertAdjacentHTML('beforeend', html);
}
function show(text)
{
showHTML("<p>" + text + "<p>");
}
function showObject(obj)
{
show("<p>" + JSON.stringify(obj) + "<p>");
}
// The real code
var keyList = [ 'a', 'b', 'c', 'd' ];
var incompleteJson = { "a": 1, "b": 2 };
var fullJson = { "a": 1, "b": 2, "c": true, "d": "yes" };
// A simple way to do what is implied by the variable name
showHTML("<h3>Has All Keys</h3>");
show("Incomplete Json");
var diff = _.difference(keyList, _.keys(incompleteJson));
var hasAllKeys = diff.length === 0;
show(hasAllKeys);
show("Full Json");
diff = _.difference(keyList, _.keys(fullJson));
hasAllKeys = diff.length === 0;
show(hasAllKeys);
// What you try to do
showHTML("<h3>Consume Expected Keys</h3>");
var localLodash = _.runInContext();
localLodash.mixin(
{
'reverseDiff': _.rearg(_.difference, [1, 0]) // switching order of arguments
}
);
show("Incomplete Json");
var expectedKeys = localLodash.chain(incompleteJson)
.keys()
.reverseDiff(keyList)
.value();
showObject(expectedKeys);
show("Full Json");
expectedKeys = localLodash.chain(fullJson)
.keys()
.reverseDiff(keyList)
.value();
showObject(expectedKeys);
<div id="pls-results"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.10.1/lodash.js"></script>
Upvotes: 2