Drun
Drun

Reputation: 619

intersection of objects except for one attribute between two arrays

I've two arrays which contain the same kind of objects (same attributes but different values associated). I want to compare the two arrays and match the objects that are equal except for one property. Then I want to find the index of the objects-matched in the first array in order to push those objects into a different array.

I think that all of this can be made using lodash and I would like to avoid for loops, in order to make it the more efficient as possible

Note that I am working in a js class

Here's my attempt (not working)

class MatchPlayers {
    constructor(){
        this.TeamA = [
            {weight:'75',height:'170', foot:'Left', available:true},
            {weight:'88',height:'190', foot:'Right', available:true},
            {weight:'65',height:'163', foot:'Right', available:false},
            {weight:'70',height:'168', foot:'Left', available:true}
        ]

        this.TeamB = [
            {weight:'75',height:'170', foot:'', available:true},
            {weight:'93',height:'201', foot:'', available:true},
            {weight:'65',height:'163', foot:'', available:false}
        ]

        this.MatchedPlayers = []
    }

    PlayersMatch (){
        for(this.i=0;this.i<this.TeamA.length;this.i++){
            if (_.intersection(this.TeamA,{weight:this.TeamB.weight, height:this.TeamB.height, available:this.TeamB.available})){
              this.position = _.findIndex(this.TeamA,{weight:this.TeamB.weight, height:this.TeamB.height, available:this.TeamB.available})
              this.MatchedPlayers.push(this.TeamA[this.position])
            } else {console.log('No matchable players')}
          }
        console.log(this.MatchedPlayers)
    }
}

In this case I want to match the objects which have the same attributes except for "foot", so the expected output would be:

//Expected Output:
this.MatchedPlayers = [
    {weight:'75',height:'170', foot:'Left', available:true}, 
    {weight:'65',height:'163', foot:'Right', available:false}
]

Upvotes: 3

Views: 744

Answers (2)

Ori Drori
Ori Drori

Reputation: 192607

To create an intersection with a custom behaviour, use _.intersectionWith(). The method accepts a predicate that compares between two objects. In this case the comparison is almost identical to _.isEqual(), but it should ignore a property.

You can create the predicate with _.isEqualWith(), which accepts a customizer function. If the compared key (the 3rd param) is foot it should return true (equal because we don't care), if not it should return undefined so that _.isEqualWith() can make the standard _.isEqual() comparisons.

const a = [{ weight: '75', height: '170', foot: 'Left', available: true }, { weight: '88', height: '190', foot: 'Right', available: true }, { weight: '65', height: '163', foot: 'Right', available: false }, { weight: '70', height: '168', foot: 'Left', available: true }];
const b = [{ weight: '75', height: '170', foot: '', available: true }, { weight: '93', height: '201', foot: '', available: true }, { weight: '65', height: '163', foot: '', available: false }];

const result = _.intersectionWith(a, b, 
  _.partialRight(_.isEqualWith,
    (...args) => args[2] === 'foot' ? true : undefined
  )
);

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

Upvotes: 0

Nina Scholz
Nina Scholz

Reputation: 386756

You could take a simplified approach and omit foot property and get the intersection by _.isEqual for the lefot over properties.

var a = [{ weight: '75', height: '170', foot: 'Left', available: true }, { weight: '88', height: '190', foot: 'Right', available: true }, { weight: '65', height: '163', foot: 'Right', available: false }, { weight: '70', height: '168', foot: 'Left', available: true }],
    b = [{ weight: '75', height: '170', foot: '', available: true }, { weight: '93', height: '201', foot: '', available: true }, { weight: '65', height: '163', foot: '', available: false }],
    omitFoot = o => _.omit(o, 'foot'),
    intersection = _.intersectionWith(
        _.map(a, omitFoot),
        _.map(b, omitFoot),
        _.isEqual
    );

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

Upvotes: 2

Related Questions