CroaToa
CroaToa

Reputation: 910

Compare array of objects: Ramda way

There are 2 arrays of objects, the first one is

const blocks = [
  { id: 1 },
  { id: 2 },
  { id: 3 },
]

and the second one is

const containers = [
  { block: { id: 1 } },
  { block: { id: 2 } },
  { block: { id: 3 } },
]

I want to take blocks array, take each object from that and find if containers array has block with such id. So if at least one id not found then I want to break loop and return false, otherwise if all id's found return true.

I've tried to implement this with .some() function but I couldn't break loop when id is not found.

I would appreciate if you advise Ramda way to mplement this.

Thank you.

Upvotes: 1

Views: 3398

Answers (5)

Akrion
Akrion

Reputation: 18515

You can achieve this with JS/Lodash/Ramda almost same exact way.

JS/Lodash having the same exact methods for every and some and in Ramda with all and any:

const blocks = [{ id: 1 },{ id: 2 },{ id: 3 }]
const containers = [{ block: { id: 1 } },{ block: { id: 2 } },{ block: { id: 3 } }]

const blocks2 = [{ id: 1 },{ id: 2 },{ id: 3 }]
const containers2 = [{ block: { id: 4 } },{ block: { id: 2 } },{ block: { id: 3 } }]

let hasAllBlocks = (blks, conts) => 
   blks.every(b => conts.some(c => c.block.id === b.id))

let hasAllBlocksLD = (blks, conts) => 
  _.every(blks, b => _.some(conts, c => c.block.id === b.id))

let hasAllBlocksR = (blks, conts) => 
   R.all(b => R.any(c => c.block.id === b.id, conts), blks)

console.log(hasAllBlocks(blocks, containers))   // true
console.log(hasAllBlocks(blocks2, containers2)) // false

console.log(hasAllBlocksLD(blocks, containers))   // true
console.log(hasAllBlocksLD(blocks2, containers2)) // false

console.log(hasAllBlocksR(blocks, containers))   // true
console.log(hasAllBlocksR(blocks2, containers2)) // false
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>

Upvotes: 0

Scott Sauyet
Scott Sauyet

Reputation: 50797

There is no particular Ramda WayTM. Ramda is a library, a toolkit, or in Underscore's description, a toolbelt. It is built to make a certain style of coding easier to read and write in JS. But it is not meant to dictate at all how you write your code.

Ramda's biggest goal is to make it easier to build applications through composing functions, ensuring such FP goodies as immutability and referential transparency.

So for this problem, I might start with something like the following:

const hasBlock = (containers) => {
  const ids = containers .map (c => c.block.id)
  return (block) => ids .includes (block.id)
}

const allContained = (containers) => (blocks) => 
  blocks .every (hasBlock (containers) )

That latter function can be quickly rewritten using Ramda to

const allContained = (containers) => (blocks) =>
  all(hasBlock(containers), blocks)

which simplifies to

const allContained = (containers) => all(hasBlock(containers))

and from there to the elegant:

const allContained = compose (all, hasBlock)

But I don't see any straightforward simplifications of hasBlock. I might on some days write it as

const hasBlock = (containers, ids = containers .map (c => c.block.id) ) => 
  (block) => ids .includes (block.id)

but that's only to satisfy some internal craving for single-expression function bodies, and not for any really good reason. It doesn't make the code any cleaner, and for some readers might make it more obscure.

So here is where I end up:

const hasBlock = (containers) => {
  const ids = containers .map (c => c.block.id)
  return (block) => ids .includes (block.id)
}

const allContained = compose (all, hasBlock)

const containers = [{block: {id: 1}}, {block: {id: 2}}, {block: {id: 4}}]

console .log (
  allContained (containers) ([{id: 1}, {id: 2}, {id: 3}]), //=> false
  allContained (containers) ([{id: 1}, {id: 2}, {id: 4}]), //=> true
  allContained (containers) ([{id: 4}, {id: 1}]),          //=> true
  allContained (containers) ([{id: 42}]),                  //=> false
)
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script><script>
const {compose, all} = R      </script>

Upvotes: 0

Krantisinh
Krantisinh

Reputation: 1729

You can do this using R.differenceWith:

const blocks = [
  { id: 1 },
  { id: 2 },
  { id: 3 },
];

const containers = [
  { block: { id: 1 } },
  { block: { id: 2 } },
  { block: { id: 4 } },
];

const diff = R.differenceWith((x,y) => x.id === y.block.id);
const mismatch = diff(blocks, containers).length > 0;

console.log(mismatch);
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>

Upvotes: 1

Maheer Ali
Maheer Ali

Reputation: 36574

You can use the equals() method.

const blocks = [
  { id: 1 },
  { id: 2 },
  { id: 3 },
]

const containers = [
  { block: { id: 1 } },
  { block: { id: 2 } },
  { block: { id: 3 } },
]

console.log(R.equals(containers.map(x => x.block.id),blocks.map(x => x.id)))
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>

Upvotes: 0

Nina Scholz
Nina Scholz

Reputation: 386604

In a non ramda way, you could compare each object with the other items and use a deep check.

const
    deep = id => o =>
        o && typeof o === 'object' && (o.id === id || Object.values(o).some(deep(id))),
    compare = (source, target) => source.every(({ id }) => target.some(deep(id))),
    blocks = [ { id: 1 }, { id: 2 }, { id: 3 }],
    containers = [{ block: { id: 1 } }, { block: { id: 2 } }, { block: { id: 3 } }]

console.log(compare(blocks, containers));

Upvotes: 0

Related Questions