如何用更优雅的Lodash方式反转对象并保留原键到数组?
Question
I want to invert an object using Lodash's _.invert method, but instead of losing duplicate value keys like the default _.invert does, I want to collect the original keys into arrays. I've implemented a function myself, but I feel it's not elegant enough. I'm wondering if there's a more concise, Lodash-idiomatic way to achieve this.
Here's my current implementation:
let tokens = { y: 'years', Y: 'years', M: 'months', mo: 'months', Mo: 'months', w: 'weeks', W: 'weeks', d: 'days', D: 'days', h: 'hours', H: 'hours', m: 'minutes', mi: 'minutes', Mi: 'minutes', s: 'seconds', S: 'ms' }, print = JSON.stringify(inverter(tokens), null, 2); document.querySelector('#test').innerHTML = print; function inverter(obj) { let out = {}; _.forOwn(obj, (o, k) => { out[o] = _.concat(out[o] || [], [k]); }); return out; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.5/lodash.min.js"></script> <pre id="test"></pre> --- ## Answer Absolutely! There's a much more Lodash-idiomatic way to do this using `_.transform`—a method specifically designed for transforming objects/arrays into new structures. It's cleaner than manually initializing an empty object and looping through it, and fits better with Lodash's functional programming style. Here's the optimized version: ```javascript function inverter(obj) { return _.transform(obj, (result, value, key) => { // Push the key to the array; initialize the array if it doesn't exist _.push(result[value] || (result[value] = []), key); }, {}); }
If you prefer a more compact one-liner (though readability should take priority), you can write:
const inverter = obj => _.transform(obj, (res, val, key) => _.push(res[val] || (res[val] = []), key), {});
Why this is better:
- Lodash-aligned:
_.transformis purpose-built for object transformation scenarios, replacing the manual accumulator + loop pattern with a more concise, idiomatic approach. - Less redundant logic: The short-circuit assignment
result[value] || (result[value] = [])eliminates the need for separate checks and_.concatcalls, making the code more efficient. - Pure functional style: Avoids modifying an external variable (the
outobject in your original code), keeping the logic self-contained.
As an alternative, you could also use _.groupBy combined with _.mapValues, though this involves two passes over the object (so _.transform is slightly more performant):
function inverter(obj) { return _.mapValues(_.groupBy(obj, val => val), group => _.map(group, (_, key) => key)); }
Both approaches will produce the desired output:
{ "years": ["y", "Y"], "months": ["M", "mo", "Mo"], "weeks": ["w", "W"], "days": ["d", "D"], "hours": ["h", "H"], "minutes": ["m", "mi", "Mi"], "seconds": ["s"], "ms": ["S"] }
内容的提问来源于stack exchange,提问作者tao

