admin管理员组

文章数量:1301534

I'm new to Ramda and currying but would like to bine two different arrays. Given:

var array1 = [
 {_id: 0, x: 10},
 {_id: 1, x: 30}
];

var array2 = [
  {UUID: 0, y: 20},
  {UUID: 1, y: 60}
];

I would like to join them where _id === UUID to get something like this:

var result = [
  {_id: 0, UUID: 0, x: 10, y: 20},
  {_id: 1, UUID: 1, x: 30, y: 60}
];

I looked at the docs and where, intersection, and merge seemed to be the ones to use. But intersection and merge expect the same key. where seems to be the ticket, but I'm still not sure how to tell it that I want two different keys to have the same value.

So I figure I can do it mirroring the POJS way, using one or more of forEach, find, and equals. Here's what I was thinking (no intersection or where usage so far):

import r from 'ramda';
var result = 
  r.forEach(
    r.find(
      r.equals(
        r.prop('UUID', r._),
        r.prop('_id', r._)
      ), data, metadata);

I'm stuck at just how to check for the separate keys (UUID and _id) from the two arrays while keeping the functional notation. I believe I'm going about it all wrong but my mind is rutted and I'll try a plain Javascript approach first.

I'm new to Ramda and currying but would like to bine two different arrays. Given:

var array1 = [
 {_id: 0, x: 10},
 {_id: 1, x: 30}
];

var array2 = [
  {UUID: 0, y: 20},
  {UUID: 1, y: 60}
];

I would like to join them where _id === UUID to get something like this:

var result = [
  {_id: 0, UUID: 0, x: 10, y: 20},
  {_id: 1, UUID: 1, x: 30, y: 60}
];

I looked at the docs and where, intersection, and merge seemed to be the ones to use. But intersection and merge expect the same key. where seems to be the ticket, but I'm still not sure how to tell it that I want two different keys to have the same value.

So I figure I can do it mirroring the POJS way, using one or more of forEach, find, and equals. Here's what I was thinking (no intersection or where usage so far):

import r from 'ramda';
var result = 
  r.forEach(
    r.find(
      r.equals(
        r.prop('UUID', r._),
        r.prop('_id', r._)
      ), data, metadata);

I'm stuck at just how to check for the separate keys (UUID and _id) from the two arrays while keeping the functional notation. I believe I'm going about it all wrong but my mind is rutted and I'll try a plain Javascript approach first.

Share Improve this question edited Jun 2, 2016 at 16:26 Nick asked Jun 2, 2016 at 16:17 NickNick 5,1981 gold badge43 silver badges68 bronze badges 4
  • The answer greatly depends on whether there is a match every time with no gaps. If so use sort and zip/reduce. – Jared Smith Commented Jun 2, 2016 at 16:30
  • @Nick, I can suggest a working native javascript solution – RomanPerekhrest Commented Jun 2, 2016 at 17:05
  • @JaredSmith There will be a match each time, as well as no duplicate/colliding keys. – Nick Commented Jun 2, 2016 at 17:35
  • @Nick see my updated answer then. With Ramda its a one-liner. – Jared Smith Commented Jun 2, 2016 at 17:41
Add a ment  | 

3 Answers 3

Reset to default 9

One could use R.indexBy, R.mergeWith, and R.merge:

//  propMerge :: String -> String -> Array Object -> Array Object -> Array Object
var propMerge = R.curry(function(k1, k2, xs1, xs2) {
  return R.values(R.mergeWith(R.merge,
                              R.indexBy(R.prop(k1), xs1),
                              R.indexBy(R.prop(k2), xs2)));
});

Since you had in mind to

" try a plain Javascript approach first."

here is a native JavaScript alternative solution using Array.concat, Array.find, Array.indexOf, Array.map, Object.keys and Object.assign functions:

var keys = ['_id', 'UUID'], result = {};

array1.concat(array2).forEach(function(obj){
    var value = obj[Object.keys(obj).find((v) => keys.indexOf(v) !== -1 )],
        props = Object.keys(obj);  // array of property names of the current object

    result[value] = result[value] || obj;
    if (Object.keys(result[value]).indexOf(props[0]) === -1) {
        Object.assign(result[value], obj);  // "merging" two objects
    }
}, result);
result = Object.keys(result).map((k) => result[k]);

console.log(JSON.stringify(result, 0, 4));

The output:

[
    {
        "_id": 0,
        "x": 10,
        "UUID": 0,
        "y": 20
    },
    {
        "_id": 1,
        "x": 30,
        "UUID": 1,
        "y": 60
    }
]

What you want is likely to use generators. Like so:

arr1.sort((a, b) => a._id - b._id);
arr2.sort((a, b) => a.UUID - b.UUID);

let step1 = arr1[Symbol.iterator]();
let step2 = arr2[Symbol.iterator]();

let zipSeq = (first, second) => {
  let done = false, result = [];
  while (!done) {
    let obj = first.next();
    let check = second.next();
    while (check.value.UUID !== obj.value._id && !check.done) {
      check = second.next(); // make sure we account for non-sequential
    }
    result.push(Object.assign({}, obj.value, check.value));
    done = obj.done || check.done;
  }
  return result;
};

let mixed = zipSeq(step1, step2);

Generalizing the zipSeq function to take arbitrary generators and a bining callback is left as an exercize to the reader.

There are certainly more pact ways to solve this than lazy sequences, but its pretty short and almost certainly more performant than repeatedly searching the second array for matches, and its far less imperative than maintaining and incrementing two separate indexing variables to do both in one pass.

Since per your ment there are always matches, you can sort as above and then do this:

let results = R.zip(arr1, arr2, (a, b) => Object.assign({}, a, b));

本文标签: javascriptCombine two arrays with two different keys with RamdaStack Overflow