Radex
Radex

Reputation: 8587

Compare content of two array

I need to compare content for two array (source/target) of objects.

If an item in source does not exist in target, I should add item only to include.

If an item in source does exist in target, I should add item only to exclude.

Currently using ramda R.differenceWith() but I get an issue when target is empty.

I would to know if differenceWith fit the porpoise here, or I could use another functions. Please provide me an example thanks!

Notes: An answer even without using ramda is ok.

Demo

// source
  const pathHash1 = {
    hash: "c4ca4238a0b923820dcc509a6f75849b",
    path: "./source/file1.txt"
  };
  const pathHash2 = {
    hash: "c81e728d9d4c2f636f067f89cc14862c",
    path: "./source/file2.txt"
  };
  const pathHash3 = {
    hash: "eccbc87e4b5ce2fe28308fd9f2a7baf3",
    path: "./souce/file3.txt"
  };
  // target
  const pathHash4 = {
    hash: "eccbc87e4b5ce2fe28308fd9f2a7baf3",
    path: "./target/file3.txt"
  };

// works
// const source = [pathHash1, pathHash2, pathHash3]
// const target = [pathHash4]

// does no work
const source = [pathHash1, pathHash2, pathHash3]
const target = []

// result pathHash1, pathHash2
const resultInclude = R.differenceWith((x,y)=> x.hash === y.hash, source, target)
const resultExclude= R.differenceWith((x,y)=> x.hash !== y.hash, source, target)

console.log('include',resultInclude.map(x=>x.hash))
console.log('exclude',resultExclude.map(x=>x.hash))

Upvotes: 1

Views: 332

Answers (5)

Scott Christopher
Scott Christopher

Reputation: 6516

Another option here is to build up a native Set of the target hashes and use R.partition to split the source list into two lists depending on whether or not the hash exists in the Set.

const source = [
  {
    hash: "c4ca4238a0b923820dcc509a6f75849b",
    path: "./source/file1.txt"
  },
  {
    hash: "c81e728d9d4c2f636f067f89cc14862c",
    path: "./source/file2.txt"
  },
  {
    hash: "eccbc87e4b5ce2fe28308fd9f2a7baf3",
    path: "./souce/file3.txt"
  }
]

const target = [
  {
    hash: "eccbc87e4b5ce2fe28308fd9f2a7baf3",
    path: "./target/file3.txt"
  }
]

////

const targetHashes =
  target.reduce((hashes, next) => hashes.add(next.hash), new Set)

const [resultExclude, resultInclude] =
  R.partition(x => targetHashes.has(x.hash), source)

////

console.log("resultInclude", resultInclude)
console.log("resultExclude", resultExclude)
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.min.js"></script>

Upvotes: 0

GibboK
GibboK

Reputation: 73918

In vanilla js you could consider using this version:

  const resultInclude = pathHashListSource.filter(x => !pathHashListTarget.find(y => y.hash === x.hash));
  const resultExclude = pathHashListSource.filter(x => pathHashListTarget.find(y => y.hash === x.hash));

Upvotes: 1

Ori Drori
Ori Drori

Reputation: 191976

Use R.innerJoin for the exclude case:

const getInclude = R.differenceWith(R.eqProps('hash'))
const getExclude = R.innerJoin(R.eqProps('hash'))

const pathHash1 = {hash: "c4ca4238a0b923820dcc509a6f75849b",path: "./source/file1.txt"},pathHash2 = {hash: "c81e728d9d4c2f636f067f89cc14862c",path: "./source/file2.txt"},pathHash3 = {hash: "eccbc87e4b5ce2fe28308fd9f2a7baf3",path: "./souce/file3.txt"},pathHash4 = {hash: "eccbc87e4b5ce2fe28308fd9f2a7baf3",path: "./target/file3.txt"}

const source1 = [pathHash1, pathHash2, pathHash3]
const target1 = [pathHash4]

const source2 = [pathHash1, pathHash2, pathHash3]
const target2 = []

const getHash = R.map(R.prop('hash'))

console.log('include1', getHash(getInclude(source1, target1)))
console.log('exclude1', getHash(getExclude(source1, target1)))

console.log('include2', getHash(getInclude(source2, target2)))
console.log('exclude2', getHash(getExclude(source2, target2)))
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.min.js"></script>

Upvotes: 3

gaetanoM
gaetanoM

Reputation: 42054

The result you get from R.differenceWith() is correct because the source of such a method is:

var differenceWith = _curry3(function differenceWith(pred, first, second) {
    var out = [];
    var idx = 0;
    var firstLen = first.length;
    while (idx < firstLen) {
            if (!_includesWith(pred, first[idx], second) && 
                !_includesWith(pred, first[idx], out)) {
                out.push(first[idx]);
            }
            idx += 1;
    }
    return out;
});

Like you can see the difference is computed using _includesWith. But, while the second array is empty the on going output array is going to be be filled (no duplicates).

// source
const pathHash1 = {
    hash: "c4ca4238a0b923820dcc509a6f75849b",
    path: "./source/file1.txt"
};
const pathHash2 = {
    hash: "c81e728d9d4c2f636f067f89cc14862c",
    path: "./source/file2.txt"
};
const pathHash3 = {
    hash: "eccbc87e4b5ce2fe28308fd9f2a7baf3",
    path: "./souce/file3.txt"
};
// target
const pathHash4 = {
    hash: "eccbc87e4b5ce2fe28308fd9f2a7baf3",
    path: "./target/file3.txt"
};

// works
// const source = [pathHash1, pathHash2, pathHash3]
// const target = [pathHash4]

// issue
const source = [pathHash1, pathHash2, pathHash3]
const target = []

// result pathHash1, pathHash2
const resultInclude = R.differenceWith((x,y)=> x.hash === y.hash, source, target)
const resultExclude= R.differenceWith((x,y)=> x.hash !== y.hash, source, target)


console.log('include',resultInclude.map(x=>x.hash))
console.log('exclude',resultExclude.map(x=>x.hash))
<script src="https://cdn.jsdelivr.net/npm/ramda@latest/dist/ramda.min.js"></script>

Upvotes: 0

customcommander
customcommander

Reputation: 18901

I think you could use difference instead of differenceWith for simple objects. If you want to find the common objects in both source and target, I'd suggest using innerJoin:

const {difference, innerJoin, equals} = R;

const a = [{x: 1}, {y: 2}, {z: 3}];
const b = [{a: 0}, {x: 1}];

console.log(
  difference(a, b)
);

console.log(
  innerJoin(equals, a, b)
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.min.js"></script>

Upvotes: 0

Related Questions