简体   繁体   中英

Compare two deeply nested arrays in javascript using lodash

I have two arrays as follows:

const initBlocks = [
    {
        closingDays: [
            {start: '01/10/2020', stop: '10/10/2020', type: 'workplace', reference: '', uuid: ''}
        ],
        openingsHours: [
            { start: '07:00', stop: '12:30', type: 'workplace', day: 'mon', reference: '', uuid: ''}
            { start: '07:00', stop: '12:30', type: 'workplace', day: 'tue', reference: '', uuid: ''}
        ],
        order: 0,
        reference: "123"
    }
    {
        openingsHours: [
            {start: '07:00', stop: '12:30', type: 'workplace', day: 'mon', reference: '', uuid: ''}
        ],
        order: 0,
        reference: "123"
    }
]

And the second one:

 const blocks = [
    {
        closingDays: [
            {start: '01/10/2020', stop: '10/10/2020', type: 'workplace', reference: '', uuid: ''}
        ],
        openingsHours: [
            { start: '07:00', stop: '12:30', type: 'workplace', day: 'mon', reference: '', uuid: ''}
            { start: '07:00', stop: '12:30', type: 'showroom', day: 'tue', reference: '', uuid: ''}
        ],
        order: 0,
        reference: "123"
    }
    {
        openingsHours: [
            {start: '07:00', stop: '12:30', type: 'workplace', day: 'mon', reference: '', uuid: ''}
            {start: '07:00', stop: '12:30', type: 'workplace', day: 'mon', reference: '', uuid: ''}
        ],
        order: 1,
        reference: "321"
    }
]

I want to compare those two and I want to see if openingsHours are the same. It can be more objects in the array, then the properties (start, stop, type and day) can be different.

I've tried something as follows:

let areEqual = true;
filter(blocks, block => {
    return filter(initBlocks, initBlock => {
        const blockHours = block.openingsHours.map(item => { return { ...omit(item, ['reference', 'uuid']) } })
        const initBlockHours = initBlock.openingsHours.map(item => { return { ...omit(item, ['reference', 'uuid']) } })
        if(!isEqual(blockHours, initBlockHours)){
            areEqual = false
        }
    })
})

But the result in areEqual is not what it should be. They are not the same thus it should be false , but I'm getting true . Any idea?

As suggested in the comments, you should start out with areEqual set to false . Then, we only change it if there are values that are equal, and if they are equal, we immediately return.

const { filter, isEqual, omit } = require( "lodash" );
const initBlocks = [
  {
    closingDays: [
      {
        start: "01/10/2020",
        stop: "10/10/2020",
        type: "workplace",
        reference: "",
        uuid: ""
      }
    ],
    openingsHours: [
      {
        start: "07:00",
        stop: "12:30",
        type: "workplace",
        day: "mon",
        reference: "",
        uuid: ""
      },
      {
        start: "07:00",
        stop: "12:30",
        type: "workplace",
        day: "tue",
        reference: "",
        uuid: ""
      }
    ],
    order: 0,
    reference: "123"
  },
  {
    openingsHours: [
      {
        start: "07:00",
        stop: "12:30",
        type: "workplace",
        day: "mon",
        reference: "",
        uuid: ""
      }
    ],
    order: 0,
    reference: "123"
  }
];
const blocks = [
  {
    closingDays: [
      {
        start: "01/10/2020",
        stop: "10/10/2020",
        type: "workplace",
        reference: "",
        uuid: ""
      }
    ],
    openingsHours: [
      {
        start: "07:00",
        stop: "12:30",
        type: "workplace",
        day: "mon",
        reference: "",
        uuid: ""
      },
      {
        start: "07:00",
        stop: "12:30",
        type: "showroom",
        day: "tue",
        reference: "",
        uuid: ""
      }
    ],
    order: 0,
    reference: "123"
  },
  {
    openingsHours: [
      {
        start: "07:00",
        stop: "12:30",
        type: "workplace",
        day: "mon",
        reference: "",
        uuid: ""
      },
      {
        start: "07:00",
        stop: "12:30",
        type: "workplace",
        day: "mon",
        reference: "",
        uuid: ""
      }
    ],
    order: 1,
    reference: "321"
  }
];

let areEqual = false;

filter( blocks, block => {
  return filter( initBlocks, initBlock => {
    const blockHours = block.openingsHours.map( item => {
      return { ...omit( item, [ "reference", "uuid" ] ) };
    } );

    const initBlockHours = initBlock.openingsHours.map( item => {
      return { ...omit( item, [ "reference", "uuid" ] ) };
    } );
    
    if ( isEqual( blockHours, initBlockHours ) ) {
      areEqual = true;
      return;
    }
  } );
} );

console.log(areEqual);

If the order inside the arrays does matter in the comparison you should do this:

const check = initBlocks
  .map((b, idx) => {
    const _bOpeningHours = [...b.openingsHours];
    delete _bOpeningHours.reference;
    delete _bOpeningHours.uuid;
    const _blocksOpeningHours = [...blocks[idx].openingsHours];
    delete _blocksOpeningHours.reference;
    delete _blocksOpeningHours.uuid;
    return lodash.isEqual(_bOpeningHours, _blocksOpeningHours);
  })
  .every((c) => !!c);

Codesandbox link

If the order shouldn't matter you should group the opening hours of the two arrays in one array for each array you compare, then sort them and use isEqual to get the result:

const _initBlocks = initBlocks.map(b => b.openingHours).sort()
const _blocks = blocks.map(b => b.openingHours).sort()

const result = lodash.isEqual(_initBlocks, _blocks);

Note: Do not use sort() as it is, because the openingHours contain Objets. Use a custom sort() function to sort them based on a value you want. Otherwise the isEqual() will take into account the order of the elements. Same goes for the objects inside the openingHours, you should sort them as well.

Instead of checking on all inner objects, you can try to eliminate duplicates and use a custom format with necessary value.

For comparison, I'm using following format:

{
  "workplace|mon": "07:00|12:30",
  "workplace|tue": "07:00|12:30"
}

Once you have processed to get necessary values, you can just check the equality using lodash's isEqual and return it

 function getTypeBasedHours(array) { return array.reduce((acc, item) => { item.openingsHours.forEach((workDetails) => { const key = `${workDetails.type}|${workDetails.day}`; acc[key] = `${workDetails.start}|${workDetails.stop}` }) return acc }, {}) } const initBlocks=[{closingDays:[{start:"01/10/2020",stop:"10/10/2020",type:"workplace",reference:"",uuid:""}],openingsHours:[{start:"07:00",stop:"12:30",type:"workplace",day:"mon",reference:"",uuid:""},{start:"07:00",stop:"12:30",type:"workplace",day:"tue",reference:"",uuid:""}],order:0,reference:"123"},{openingsHours:[{start:"07:00",stop:"12:30",type:"workplace",day:"mon",reference:"",uuid:""}],order:0,reference:"123"}]; const blocks=[{closingDays:[{start:"01/10/2020",stop:"10/10/2020",type:"workplace",reference:"",uuid:""}],openingsHours:[{start:"07:00",stop:"12:30",type:"workplace",day:"mon",reference:"",uuid:""},{start:"07:00",stop:"12:30",type:"showroom",day:"tue",reference:"",uuid:""}],order:0,reference:"123"},{openingsHours:[{start:"07:00",stop:"12:30",type:"workplace",day:"mon",reference:"",uuid:""},{start:"07:00",stop:"12:30",type:"workplace",day:"mon",reference:"",uuid:""}],order:1,reference:"321"}]; const map1 = getTypeBasedHours(initBlocks) const map2 = getTypeBasedHours(blocks) console.log(map1, map2) console.log(_.isEqual(map1, map2))
 <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.20/lodash.min.js"></script>

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