简体   繁体   中英

Merge objects using lodash

How would I be able to merge the following objects into one:

Object One

{"risks": [
    {
        "forests_wildlife": ["The landscape"],
        "other": ["Something here"]
    } 
]}

Object Two

{"riskDetails": [
    {
        "forests_wildlife": "The landscape",
        "affected_people": "1000-10,000",
        "affected_wildlife": "2-5 wildlife populations"
    },
    {
        "other": "Custom",
        "affected_people": "100-1000",
        "affected_wildlife": "5-10 wildlife populations"
    } 
]}

Desired Output

{"risks": [
    {
        "forests_wildlife": [
            {
                "The landscape" : {
                    "riskDetails": {
                        "affected_people": "1000-10,000",
                        "affected_wildlife": "2-5 wildlife populations"
                    }
                }
            }
        ],
        "other": [
            {
                "Custom something" : {
                    "riskDetails": {
                        "affected_people": "100-1000",
                        "affected_wildlife": "5-10 wildlife populations"
                    }
                }
            }
        ]
    },...
]}

I would really appreciate any help. Lodash or no lodash would be fine for as long as the solution works.

Edit:

I figured that for my purpose I do not really have to combine the two into one object, but could just go through each item in the first one and find a match in the second. Here's a snippet from what I came up with which worked for me:

        const riskCategory = data.risks;
        const riskDetails = data.riskDetails;
        return _.map(riskCategory, category => {
            return _.map(category, (risk, key) => {
                return _.map(risk, (item, index) => {
                    var details = _.find(riskDetails, _.matchesProperty( key, item ));
                    if ( details ) {
                        return (
                            <tr key={index}>
                                <td> {item} </td>
                                <td> {details.affected_people} </td>
                                <td> {details.affected_wildlife} </td>
                            </tr>
                        );
                    }
                    return;
                });
            });
        });

It's a nested map with find and matches somewhere in the middle to be able to get to the innermost part of the second object. Maybe there is another way to do this that is cleaner and more elegant. I am new to javascript and I find lodash is helping me make my life a little bit easier, but I don't want to rely on it too much.

Some possibile small improvements

Since you ask if it could be possible to improve on the algorithm you wrote, I tried to make some changes.

  • I named the functions starting from testcase2, as I considered yours to be testcase1.
  • please forgive me since to make testing independent of React, Babel and/or Typescript I converted the JSX syntax to a simpler string concatenation.
  • to make the functions more useful, I supposed you also wanted to flatten the resulting array and purge any falsey value from the collection in order to obtain a readily usable data format.

Can I quickly try those functions in the browser?

Of course, I actually made a Codepen where you can try this both implementations directly in your browser: just remember to open your browser console to see the test results! Here is the link: https://codepen.io/jhack_jos/pen/gOLYzvJ

Two possible implementations

Using.flattenDeep

function testcase2(data)
{
    const riskCategory = data.risks;
    const riskDetails = data.riskDetails;
    return _.chain(riskCategory)
            .map( category => {
                return _.map(category, (risk, key) => {
                    return _.map(risk, (item, index) =>
                        {
                          const details = _.find(riskDetails, [key, item]);
                          return details &&
                            "<tr key=" + index + ">" +
                            "<td> " + item + "</td>" +
                            "<td> " + details.affected_people + "</td>" +
                            "<td> " + details.affected_wildlife + "</td>" +
                            "</tr>";
                        });
                  });
            })
           .flattenDeep()
           .compact()
           .value();
}

Using.flatMap

function testcase3(data)
{
    const riskCategory = data.risks;
    const riskDetails = data.riskDetails;
    return _.chain(riskCategory)
            .flatMap( category => {
                return _.flatMap(category, (risk, key) => {
                    return _.map(risk, (item, index) =>
                        {
                          const details = _.find(riskDetails, [key, item]);
                          return details &&
                            "<tr key=" + index + ">" +
                            "<td> " + item + "</td>" +
                            "<td> " + details.affected_people + "</td>" +
                            "<td> " + details.affected_wildlife + "</td>" +
                            "</tr>";
                        });
                  });
            })
           .compact()
           .value();
}

Detailed explanation

.flattenDeep, .flatMap and Monads

Did you need your data flattened?

  1. you can certainly make use of .flattenDeep to remove all the nesting that the usage of multiple .map s generated
  2. yet, Lodash gives you an even better way: _.flatMap , which may result in slightly faster execution times, so we may as well make use of it.
  3. if you are interested in the Monad buzzword , you should know this effectively relates to the concept of Monads, even if here we do not use .flatMap to enable function composition. As a matter of fact, a Monad in its most simple form is an object that implemnts .flatMap . Long story short, the need to flatten collections after a map is more compelling that one may initially think of, and opens to many possibilities.

Other considerations

  • the _.find function already has _.matchesProperty included in the syntax _.find(array, [key, value])
  • it is better to avoid having multiple return statements in functions if possible, it makes easier to think about the algorithm and refactor
  • using the && operator we can return undefined if details is undefined . Otherwise the expression on the right is returned.
  • I see you're using const , thus reaching towards immutability. That's excellent, as a matter of fact you can also make the details variable const .
  • you can use the _.compact function to quickly remove all array elements containing falsey values like false, null, 0, "", undefined, and NaN .

Thank you for reading!

I am at your disposal, would you need any further detail or if the code failed to match what you were looking for.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM