jashkenas / underscore

JavaScript's utility _ belt
https://underscorejs.org
MIT License
27.3k stars 5.53k forks source link

how to implement forEachRight lodash feature in underscorejs ? #2901

Closed sivaprabug closed 3 years ago

sivaprabug commented 3 years ago

How to implement forEachRight lodash feature in underscorejs ?

jgonggrijp commented 3 years ago

Before I answer your question, I should emphasize that Underscore will probably never implement eachRight. The distinction between "forward" and "backward" iteration really only applies to arrays; in objects, any order over the keys is arbitrary, and in maps, sets and iterables (which we will add support for in Underscore 2.0), there is only one direction to choose from. In fact, for this same reason, I'm considering to drop _.reduceRight in Underscore 2.0. "Right-first" iteration has no future for any collection type other than arrays, for which you can already use _.findLastIndex and _.lastIndexOf.

That said, you can patch an eachRight into Underscore with just a few lines of code. I think the following is the most functional, future-proof and backwards-compatible way to do it:

// Demonstrating monolithic import here. Use modular imports if you're
// building a custom Underscore. In that case, make sure to also
// import 'underscore/modules/iteratee.js' for its side effects.
import _, { find, findLastIndex, findKey, isFunction, isNumber, compose, noop } from 'underscore';

// export default only needed if you're building a custom Underscore.
// In that case, this would be your eachRight.js.
export default function eachRight(collection, iteratee, context) {
    // Pass the result of the iteratee to _.noop to ensure that nothing is "found"
    // so that iteration continues all the way to the end.
    iteratee = compose(noop, _.iteratee(iteratee, context));
    // Optionally, prevent functions from being iterated as arrays. Currently,
    // Underscore doesn't do this but Lodash does.
    if (isFunction(collection)) {
        // Force treatment as a plain object.
        findKey(collection, iteratee);
    } else if (collection && isNumber(collection.length)) {
        // Scan the array-like collection from right to left.
        findLastIndex(collection, iteratee);
    } else {
        // Treat the collection as a plain object (or in the future, map, set or
        // iterable), just like in forward _.each.
        find(collection, iteratee);
    }
    return collection;
}

// If you just want to extend Underscore:
_.mixin({
    eachRight: eachRight,
    forEachRight: eachRight
});
// (now you can use the function in chains under both names)

// Alternatively, if you are building a custom Underscore:
// In your custom `index.js`:
export { default as eachRight, default as forEachRight } from './eachRight.js';

Note that I didn't bother to reverse the order of the keys of a plain object. This is a difference from Lodash _.eachRight and also from Underscore's _.reduceRight, but as I said before, the order of the keys doesn't really mean anything, anyway.

Lodash also has a procedural break like construction where the iteratee can return false in order to stop iteration. If you want to copy that behavior as well, you can modify the iteratee a bit:

    var internalIteratee = _.iteratee(iteratee, context);
    iteratee = function(value, key, collection) {
        return internalIteratee(value, key, collection) === false;
    };

Final note: the code I've written above supports the context argument like in all Underscore iteration functions. Lodash used to have that as well, but dropped it in version 4.

I'll close this now on the assumption that your question is answered, but please feel free to reopen if you feel the need.

sivaprabug commented 3 years ago

@jgonggrijp

In lodash - 2.4.1 eachRight() is different from your logic My underscorjs version is 1.6.0

jgonggrijp commented 3 years ago

Sure, there are differences, including the ones I already mentioned. So what exactly do you need, and why?

sivaprabug commented 3 years ago

In lodash - 2.4.1 have security vulnerability issue is there so plan to use underscorejs version is 1.6.0 , in my project other functionalities support in underscorejs 1.6.0. The only problem in forEachRight only not able construct the prototype in underscorejs 1.6.0

jgonggrijp commented 3 years ago

@gsivaprabu Could you show a few lines of code that illustrate the problem?

If at all possible, I recommend that you upgrade to Underscore 1.11 or later, because 1.6 is really outdated.

sivaprabug commented 3 years ago

@gsivaprabu Could you show a few lines of code that illustrate the problem?

If at all possible, I recommend that you upgrade to Underscore 1.11 or later, because 1.6 is really outdated.

Hi @jgonggrijp

Thanks for your valuable reply

The whole project based on 1.6 only, we update underscorejs 1.11 or later means need to re-write all modules.

Only function i struggle in lodash - 2.4.1 eachRight() only

https://lodash.com/docs/2.4.2#forEachRight

_.forEachRight(collection, [callback=identity], [thisArg])

My Scenario like below:

_.forEachRight(groupBy, this.addCreateChart, this);

Using _mixin not able to adopt the lodash 2.4.1 logic:

Ref:

https://www.npmjs.com/package/lodash/v/2.4.1

lodash 2.4.1_.forEachRight function adopt to underscorejs 1.6.0

https://www.npmjs.com/package/underscore/v/1.6.0

jgonggrijp commented 3 years ago

@gsivaprabu Please give me a bit more guidance. What error(s) do you see if you use just Underscore 1.6, and what error(s) do you see if you try to mix in the eachRight implementation I suggested? What exactly does the module look like where you pasted my eachRight implementation?

Also, are you really sure you'd need to rewrite all modules in order to use Underscore >= 1.11? There shouldn't be any hard breaking changes between 1.6 and 1.12.