Find path to object in object nested array

2020-02-07 06:26发布

问题:

I have an object, of which parameters contain and array of object. I receive 1 object id and I need to find its position in that whole mess. With procedural programming I got it working with:

const opportunitiesById =  {
  1: [
    { id: 1, name: 'offer 1' },
    { id: 2, name: 'offer 1' }
  ],
  2: [
    { id: 3, name: 'offer 1' },
    { id: 4, name: 'offer 1' }
  ],
  3: [
    { id: 5, name: 'offer 1' },
    { id: 6, name: 'offer 1' }
  ]
};

const findObjectIdByOfferId = (offerId) => {
  let opportunityId;
  let offerPosition;
  const opportunities = Object.keys(opportunitiesById);

  opportunities.forEach(opportunity => {
    const offers = opportunitiesById[opportunity];

    offers.forEach((offer, index) => {
      if (offer.id === offerId) {
        opportunityId = Number(opportunity);
        offerPosition = index;
      }
    })
  });

return { offerPosition, opportunityId };
}

console.log(findObjectIdByOfferId(6)); // returns { offerPosition: 1, opportunityId: 3 }

However this is not pretty and I want to do that in a functional way. I've looked into Ramda, and I can find an offer when I'm looking into a single array of offers, but I can't find a way to look through the entire object => each array to find the path to my offer.

R.findIndex(R.propEq('id', offerId))(opportunitiesById[1]);

The reason I need to know the path is because I then need tho modify that offer with new data and update it back where it is.

Thanks for any help

回答1:

I would transform your object into pairs.

So for example transforming this:

{ 1: [{id:10}, {id:20}],
  2: [{id:11}, {id:21}] }

into that:

[ [1, [{id:10}, {id:20}]],
  [2, [{id:11}, {id:21}]] ]

Then you can iterate over that array and reduce each array of offers to the index of the offer you're looking for. Say you're looking for offer #21, the above array would become:

[ [1, -1],
  [2,  1] ]

Then you return the first tuple which second element isn't equal to -1:

[2, 1]

Here's how I'd suggest doing this:

const opportunitiesById =  {
  1: [ { id: 10, name: 'offer 1' },
       { id: 20, name: 'offer 2' } ],
  2: [ { id: 11, name: 'offer 3' },
       { id: 21, name: 'offer 4' } ],
  3: [ { id: 12, name: 'offer 5' },
       { id: 22, name: 'offer 6' } ]
};

const findOfferPath = (id, offers) =>
  pipe(
    toPairs,
    transduce(
      compose(
        map(over(lensIndex(1), findIndex(propEq('id', id)))),
        reject(pathEq([1], -1)),
        take(1)),
      concat,
      []))
    (offers);


console.log(findOfferPath(21, opportunitiesById));
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.min.js"></script>
<script>const {pipe, transduce, compose, map, over, lensIndex, findIndex, propEq, reject, pathEq, take, concat, toPairs} = R;</script>

Then you can take that path to modify your offer as you see fit:

const opportunitiesById =  {
  1: [ { id: 10, name: 'offer 1' },
       { id: 20, name: 'offer 2' } ],
  2: [ { id: 11, name: 'offer 3' },
       { id: 21, name: 'offer 4' } ],
  3: [ { id: 12, name: 'offer 5' },
       { id: 22, name: 'offer 6' } ]
};

const updateOffer = (path, update, offers) =>
  over(lensPath(path), assoc('name', update), offers);

console.log(updateOffer(["2", 1], '