DEV Community

loading...

We have 2 arrays of objects of the same class. How to substract the 1st from the 2nd, based only on one property?

axelledrouge profile image AxelleDRouge ・2 min read

So this is the first technical post I am writing. It is mostly destined to my future self, but also, I am not really satisfied with the answer to the problem, I would love to have reviews on how I could improve the method.

I am currently working on the refactoring of an application. I have already written about it in other posts.
On of the biggest problems about that is that I am often confronted to necessity of working in the context of the application, using the pattern of the objects that the application needs, opposite to building this context.
It leads sometimes to problems to trying to improve solutions in a more efficient way than before, but still limited by the "guidelines" of the legacy code.

The problem

So after having implemented class patterns in the application, I found myself with the following problem :

I have a class with properties that will serve to populate a React Component.

The first send a simple array of string :

[
   {
      label:'label1',
      visible:false,
      otherProp:'something'
   },
   {
      label:'label2',
      visible:false,
      otherProp:'something'
   }
]

The second send an array of objects :

[
   {
      label:'label1',
      otherProp:'something'
   },
   {
      label:'label2',
      otherProp:'something'
   }
   {
      label:'label3',
      otherProp:'something'
   }
   {
      label:'label4',
      otherProp:'something'
   }
]

Both label properties refers to the same values.
And every objects will be converts to the same class.

The objective is to substract the first list from the second one, because their properties 'visible' is set at false.
And I want to do it with the smallest O that I can, and with not too much verbose.

EDIT :
To be more clear, I add the expected result of the function :

[
   {
      label:'label3',
      otherProp:'something'
   }
   {
      label:'label4',
      otherProp:'something'
   }
]

My solution

So first I identify the property of reference, here is 'label'.
I will extract the values of 'label' that must be eliminated from the bigger array.

Then I must compare the values of the result to the bigger array. Each element must be different from all the values of the smallest one.

const substractObjectsFromArray = (arrayTotal, arrayToSubstract, propertyOfReference) => {
   /* extract the list of values to substract */
   let extractValue = (arrayToSubstract, propertyOfReference) => arrayToSubstract.map(element => element[propertyOfReference]);

   /* substract the element based of the list */
   let arrayResult = (arrayTotal, arrayToSubstract, propertyOfReference) => 
       arrayTotal.filter(element => 
            extractValue(arrayToSubstract, propertyOfReference).every(ele => element.label !== ele) === true)
   return arrayResult(arrayTotal, arrayToSubstract, propertyOfReference);
}

So I tried my best to be the most efficient I could with the context I had. But I still lack a lot of experience and knowledge. Maybe there is a design pattern or data structure that could have more easily answered my problem, if so please share, I would love to learn about it.

Thanks for reading

Discussion (9)

pic
Editor guide
Collapse
dhilipkmr profile image
Dhilip kumar

I'm no expert just tried.

Not tested.

Store all the various props of arrayToSubtract in an Object.

var referenceObj = {}
arrayToSubstract.forEach((item) => referenceObj[item[propertyOfReference]] = true;);

//Reference object keys should not be in arrayTotal

var outputArr = arrayTotal.filter((item) => !referenceObj[item[propertyOfReference]);

Collapse
axelledrouge profile image
AxelleDRouge Author

Interesting way to resolve the problem, it should work :)

Collapse
khuongduybui profile image
Duy K. Bui

First of all:

  1. Why would you define functions within your function and call them each only once? I would just skip all those arrow functions.
  2. Use const in place of let unless you absolutely need to reassign these variables, which you don't in this case.

Your solution seems to work, only 2 things I can think of to improve (besides not defining functions then calling them once as mentioned above):

  1. You are calling extractValue() on the SAME set of parameters for every member of arrayTotal, that's redundant.
  2. You can replace that .every() call with .includes().

A revised version would look like this:

const substractObjectsFromArray = (arrayTotal, arrayToSubstract, propertyOfReference) => {
  const idsToSubtract = arrayToSubtract.map(element => element[propertyOfReference]);
  return arrayTotal.filter(element => idsToSubtract.includes(element[propertyOfReference]);
}
Collapse
axelledrouge profile image
AxelleDRouge Author

Thanks you are right, it's a good way to improve it.

Collapse
ankit199 profile image
Ankit kumar shukla

Merge both aaray using
Let newSet=[...set1,...set2]
Then apply this funtiin...
function unique(array, propertyName) {
return array.filter((e, i) => array.findIndex(a => a[propertyName] === e[propertyName]) === i);
}

Collapse
axelledrouge profile image
AxelleDRouge Author • Edited

Thanks for your reply.
Could you details more the process you use? Why would you use findIndex?
I tried to use your method in my use case, but it doesn't remove all the values from the smaller list in the total one
I will edit my post to make it more clearer with the expected result

Collapse
ankit199 profile image
Ankit kumar shukla

I have used findindex to find current index property value to match with passed property to make it unique

Thread Thread
axelledrouge profile image
AxelleDRouge Author

Doesn't findIndex returns the first value that match the provided testing function? In need all the elements that match the predicate, not only the first one.

Thread Thread
refschool profile image
Webdeveloper⭐⭐

a more concise way would be to use the new ES6 spread operator and Set
merge your 2 arrays then
let mergedArray = array1.concat(array2)
const unique = [...new Set(mergedArray.map(item => item.label))];