Fahad Subzwari
Fahad Subzwari

Reputation: 2325

How to find objects inside nested array of objects using lodash?

I have this data set

var records = [{
    gc: '2',
    time: 1231232423,
    cards: [{
        cardCode: '12121',
        rssi: 23
      }, {
        cardCode: '12122',
        rssi: 33
      }, {
        cardCode: '12421',
        rssi: 43
      }

    ]
  },
  {
    gc: '3',
    time: 4331232233,
    cards: [{
        cardCode: '6423',
        rssi: 23
      }, {
        cardCode: '12421',
        rssi: 13
      }

    ]
  }, , {
    gc: '4',
    time: 4331232233,
    cards: [{
        cardCode: '8524',
        rssi: 03
      },
      {
        cardCode: '6423',
        rssi: 23
      }, {
        cardCode: '12421',
        rssi: 67
      }
    ]
  }
]

I have an array(records) of objects and in each object there is an another array(cards). This means if i will always have cards array inside an object present in 'records'. So from very first i want to iterate over whole list records and compare the cards array of all objects with each other and then find the matched object's rssi value and push the object(that have minimum rssi value ) into another new array. In the same way at the end i want the array in which i have all the matching objects which have minimum rssi value. I am using lodash and have tried this

 matchedRecords =   records.forEach(record=>{
 record.cards.forEach(record=>{
  _.filter(records, _.flow(
    _.property('cards'),
     _.partialRight(_.some, { cardCode: record.cardCode })
         ));
      })
   })

My desired result would be

 [
   {
    gc : 3, 
    cards : [{
     cardCode : '12421',
     rssi : 13
    }]
   }
 ]

Note : cards array object should be compare on the bassis of cardCode key

Upvotes: 0

Views: 84

Answers (2)

Scott Sauyet
Scott Sauyet

Reputation: 50787

Updated

This version is based on the understanding reached after the long requirements discussion in the comments to the question. It does not use lodash. It's gotten complex enough that it probably should break into smaller pieces, and lodash might help with that.

It includes not just gc, but also time from the original records, and will, in fact, include anything else from there. If you want just gc, simply remove ...rest from the code.

const sharedCards = records => 
  Object.values(
    Object .entries (records .reduce (
      (a, {cards, ...rest}) => cards .reduce (
        (a, {cardCode, rssi}) => ({
          ...a, 
          [cardCode]: [...(a[cardCode] || []), {...rest, rssi}]
        }),
        a,
      ), 
      {}
    ))
    .filter ( ([code, rs]) => rs.length == records.length )
    .reduce((a, [code, rs]) => {
      const {gc, rssi, ...rest} = rs.reduce(
        (r1, r2) => r2.rssi < r1.rssi ?  r2 : r1, 
        {rssi: Infinity}
      )
      return {
        ...a, 
        [gc]: {
          ...(a[gc] || {...rest, gc}), 
          cards: [...((a[gc] || {gc}).cards || []), {cardCode: code, rssi}]
        }
      }
    }, {})  
  )
  
  
const records = [{gc: "2", time: 1231232423, cards: [{cardCode: "12121", rssi: 23}, {cardCode: "12122", rssi:33}, {cardCode: "12421", rssi: 43}]}, {gc: "3", time: 4331232233, cards: [{cardCode: "6423", rssi: 23}, {cardCode: "12421", rssi: 13}]}, {gc: "4", time: 4331232233, cards: [{cardCode: "8524", rssi: 3}, {cardCode: "6423", rssi: 23}, {cardCode: "12421", rssi: 67}]}];

console .log (
  sharedCards (records)
)

// Now we add `{cardCode: "6423", rssi: 7}` to the first record
const records2 = [{gc: "2", time: 1231232423, cards: [{cardCode: "6423", rssi: 7}, {cardCode: "12121", rssi: 23}, {cardCode: "12122", rssi:33}, {cardCode: "12421", rssi: 43}]}, {gc: "3", time: 4331232233, cards: [{cardCode: "6423", rssi: 23}, {cardCode: "12421", rssi: 13}]}, {gc: "4", time: 4331232233, cards: [{cardCode: "8524", rssi: 3}, {cardCode: "6423", rssi: 23}, {cardCode: "12421", rssi: 67}]}];

console .log (
  sharedCards (records2)
)

Original Answer

This is only a partial solution, to be updated if we can resolve the discussion in the comments. It fetches all the cards which appear in every record, and chooses the version with the lowest rssi. This may be close to the requirements, or it may not.

It doesn't use lodash. It might simplify a bit with lodash, but probably not very much.

const sharedCards = records => 
  Object .entries (records .reduce (
    (a, {cards}) => cards .reduce (
      (a, {cardCode, rssi}) => ({...a, [cardCode]: [...(a[cardCode] || []), rssi]}),
      a,
    ), 
    {}
  ))
  .filter ( ([code, rssis]) => rssis.length == records.length )
  .map ( ([code, rssis]) => ({ cardCode: code, rssi: Math .min (...rssis) }) )
    

const records = [{gc: "2", time: 1231232423, cards: [{cardCode: "12121", rssi: 23}, {cardCode: "12122", rssi:33}, {cardCode: "12421", rssi: 43}]}, {gc: "3", time: 4331232233, cards: [{cardCode: "6423", rssi: 23}, {cardCode: "12421", rssi: 13}]}, {gc: "4", time: 4331232233, cards: [{cardCode: "8524", rssi: 3}, {cardCode: "6423", rssi: 23}, {cardCode: "12421", rssi: 67}]}];

console .log (
  sharedCards (records)
)

// Now we add `{cardCode: "6423", rssi: 7}` to the first record
const records2 = [{gc: "2", time: 1231232423, cards: [{cardCode: "6423", rssi: 7}, {cardCode: "12121", rssi: 23}, {cardCode: "12122", rssi:33}, {cardCode: "12421", rssi: 43}]}, {gc: "3", time: 4331232233, cards: [{cardCode: "6423", rssi: 23}, {cardCode: "12421", rssi: 13}]}, {gc: "4", time: 4331232233, cards: [{cardCode: "8524", rssi: 3}, {cardCode: "6423", rssi: 23}, {cardCode: "12421", rssi: 67}]}];

console .log (
  sharedCards (records2)
)

Upvotes: 1

User863
User863

Reputation: 20039

With Lodash

let records = [{"gc":"2","time":1231232423,"cards":[{"cardCode":"12121","rssi":23},{"cardCode":"12122","rssi":33},{"cardCode":"12421","rssi":43}]},{"gc":"3","time":4331232233,"cards":[{"cardCode":"6423","rssi":23},{"cardCode":"12421","rssi":13}]},{"gc":"4","time":4331232233,"cards":[{"cardCode":"8524","rssi":3},{"cardCode":"6423","rssi":23},{"cardCode":"12421","rssi":67}]}]

let mapped = _.reduce(records, (acc, rec) => {
  _.forEach(rec.cards, (card) => {  
    card = _.assign({}, card, { gc: rec.gc, multiple: false })
    
    if (card.cardCode in acc) {
      let lc = acc[card.cardCode].rssi < card.rssi
      acc[card.cardCode] =  lc ? acc[card.cardCode] : card
      acc[card.cardCode].multiple = lc
    } else {
      acc[card.cardCode] = card;
    }    
  })
  return acc
}, {})

let result = _.reduce(records, (acc, rec) => {
  rec.cards = _.filter(rec.cards, (card) => mapped[card.cardCode].multiple && mapped[card.cardCode].gc === rec.gc )
  rec.cards.length && acc.push(rec)
  return acc
}, []);

console.log(result)
.as-console-wrapper {
  max-height: 100%!important
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js"></script>

Upvotes: 0

Related Questions