有没有什么方法可以"加入"两个javascript数组的内容,就像我在SQL中加入一样

Ala*_*an2 51 javascript

我有两个数组:问题和UserProfile

  • userProfiles:[]数组包含{ id, name }对象
  • questions:[]数组包含{ id, text, createdBy }对象

createdBy问题中的整数始终是其中一个id值userProfiles.

有没有一种方法可以"加入"数组,就像我使用数据库时加入两个SQL表一样.

作为最终结果我需要的是包含的数组

{ id, text, name }
Run Code Online (Sandbox Code Playgroud)

Aad*_*hah 49

我认为你想要的是一个内连接,它很简单,可以在JavaScript中实现:

const innerJoin = (xs, ys, sel) =>
    xs.reduce((zs, x) =>
    ys.reduce((zs, y) =>        // cartesian product - all combinations
    zs.concat(sel(x, y) || []), // filter out the rows and columns you want
    zs), []);
Run Code Online (Sandbox Code Playgroud)

为了演示,我们将使用以下数据集(谢谢@AshokDamani):

const userProfiles = [
    {id: 1, name: "Ashok"},
    {id: 2, name: "Amit"},
    {id: 3, name: "Rajeev"},
];

const questions = [
    {id: 1, text: "text1", createdBy: 2},
    {id: 2, text: "text2", createdBy: 2},
    {id: 3, text: "text3", createdBy: 1},
    {id: 4, text: "text4", createdBy: 2},
    {id: 5, text: "text5", createdBy: 3},
    {id: 6, text: "text6", createdBy: 3},
];
Run Code Online (Sandbox Code Playgroud)

这是你如何使用它:

const result = innerJoin(userProfiles, questions,
    ({id: uid, name}, {id, text, createdBy}) =>
        createdBy === uid && {id, text, name});
Run Code Online (Sandbox Code Playgroud)

在SQL术语中,这类似于:

SELECT questions.id, questions.text, userProfiles.name
FROM userProfiles INNER JOIN questions
ON questions.createdBy = userProfiles.id;
Run Code Online (Sandbox Code Playgroud)

把它们放在一起:

const innerJoin = (xs, ys, sel) =>
    xs.reduce((zs, x) =>
    ys.reduce((zs, y) =>        // cartesian product - all combinations
    zs.concat(sel(x, y) || []), // filter out the rows and columns you want
    zs), []);

const userProfiles = [
    {id: 1, name: "Ashok"},
    {id: 2, name: "Amit"},
    {id: 3, name: "Rajeev"},
];

const questions = [
    {id: 1, text: "text1", createdBy: 2},
    {id: 2, text: "text2", createdBy: 2},
    {id: 3, text: "text3", createdBy: 1},
    {id: 4, text: "text4", createdBy: 2},
    {id: 5, text: "text5", createdBy: 3},
    {id: 6, text: "text6", createdBy: 3},
];

const result = innerJoin(userProfiles, questions,
    ({id: uid, name}, {id, text, createdBy}) =>
        createdBy === uid && {id, text, name});

console.log("Open your browser console to see the output.");

console.table(result);
Run Code Online (Sandbox Code Playgroud)


编辑:但这不是最好的解决方案.由于上述解决方案循环通过笛卡尔积,因此O(m × n)运行需要时间.通过一些修改我们可以让它及时运行O(m + n)- @pebbl首先找到它:

const equijoin = (xs, ys, primary, foreign, sel) => {
    const ix = xs.reduce((ix, row) => // loop through m items
        ix.set(row[primary], row),    // populate index for primary table
    new Map);                         // create an index for primary table

    return ys.map(row =>              // loop through n items
        sel(ix.get(row[foreign]),     // get corresponding row from primary
        row));                        // select only the columns you need
};
Run Code Online (Sandbox Code Playgroud)

现在您可以按如下方式使用它:

const result = equijoin(userProfiles, questions, "id", "createdBy",
    ({name}, {id, text}) => ({id, text, name}));
Run Code Online (Sandbox Code Playgroud)

把它们放在一起:

const equijoin = (xs, ys, primary, foreign, sel) => {
    const ix = xs.reduce((ix, row) => ix.set(row[primary], row), new Map);
    return ys.map(row => sel(ix.get(row[foreign]), row));
};

const userProfiles = [
    {id: 1, name: "Ashok"},
    {id: 2, name: "Amit"},
    {id: 3, name: "Rajeev"},
];

const questions = [
    {id: 1, text: "text1", createdBy: 2},
    {id: 2, text: "text2", createdBy: 2},
    {id: 3, text: "text3", createdBy: 1},
    {id: 4, text: "text4", createdBy: 2},
    {id: 5, text: "text5", createdBy: 3},
    {id: 6, text: "text6", createdBy: 3},
];

const result = equijoin(userProfiles, questions, "id", "createdBy",
    ({name}, {id, text}) => ({id, text, name}));

console.log("Open your browser console to see the output.");

console.table(result);
Run Code Online (Sandbox Code Playgroud)

  • 你已经在这个答案中获得了+1,它应该被投票更高但是非常可读并且使用回调是一个很好的接触. (3认同)
  • 非常好的模式,用于结合来自不同API的对象属性+1 (2认同)

Jos*_*ers 15

这似乎是一个重要的通用问题,虽然有很多答案,但有些还有边界行为,如修改现有数据,解决与手头问题完全不同的问题,使用多达93,057字节的JavaScript(更不用说产生错误的结果),产生过于复杂的额外数据结构嵌套,每次调用都需要大量代码,而且最严重的是,这不是解决这个问题核心的重要的更通用问题的独立解决方案.

因此,为了更好或更坏,我编写了一个垫片,它扩展了JavaScript Array对象的方法,.joinWith旨在将this对象that数组与对象数组(by一个常见的索引字段)连接起来.可以select输出所需的字段列表(适用于仅需要少数几个字段时合并多个字段的对象)或omit输出中的字段列表(适用于需要大多数字段时合并对象数组但是很少不是).

垫片代码看起来不漂亮,所以它将在最后,以及如何将它用于OP首先出现的特定类型数据的示例:

/* this line will produce the array of objects as desired by the OP */
joined_objects_array = userProfiles.joinWith(questions, 'id', ['createdBy'], 'omit');

/* edit: I just want to make 100% sure that this solution works for you, i.e.,
 *       does exactly what you need. I haven't seen your actual data, so it's
 *       possible that your IDs are are not in common, (i.e., your createdBy
 *       is in common like you said, but not the IDs, and if so you could
 *       morph your data first like this:
 * questions.map(function(x) { x.id = x.createdBy; });
 *       before joining the arrays of objects together.
 *
 */
Run Code Online (Sandbox Code Playgroud)

以下代码用于演示:

var array1 = [{ id: 3124, name: 'Mr. Smith' },
              { id: 710, name: 'Mrs. Jones' }];
var array2 = [{ id: 3124, text: 'wow', createdBy: 'Mr. Jones' },
              { id: 710, text: 'amazing' }];

var results_all = array1.joinWith(array2, 'id');

// [{id:3124, name:"Mr. Smith", text:"wow", createdBy:"Mr. Jones"},
// {id:710, name:"Mrs. Jones", text:"amazing"}]*

var results_selected = array1.joinWith(array2, 'id', ['id', 'text', 'name']);

// [{id:3124, name:"Mr. Smith", text:"wow"},
// {id:710, name:"Mrs. Jones", text:"amazing"}]*

/* or equivalently, */
var results_omitted = array1.joinWith(array2, 'id', ['createdBy'], 1);

// [{id:3124, name:"Mr. Smith", text:"wow"},
// {id:710, name:"Mrs. Jones", text:"amazing"}]*
Run Code Online (Sandbox Code Playgroud)

这个解决方案还有一些其他好处(其中一个保留了通过索引键访问结果数据的能力,尽管返回了一个数组).

请享用!

/* Array.joinWith - shim by Joseph Myers 7/6/2013 */


if (!Array.prototype.joinWith) {
    +function () {
        Array.prototype.joinWith = function(that, by, select, omit) {
            var together = [], length = 0;
            if (select) select.map(function(x){select[x] = 1;});
            function fields(it) {
                var f = {}, k;
                for (k in it) {
                    if (!select) { f[k] = 1; continue; }
                    if (omit ? !select[k] : select[k]) f[k] = 1;
                }
                return f;
            }
            function add(it) {
                var pkey = '.'+it[by], pobj = {};
                if (!together[pkey]) together[pkey] = pobj,
                    together[length++] = pobj;
                pobj = together[pkey];
                for (var k in fields(it))
                    pobj[k] = it[k];
            }
            this.map(add);
            that.map(add);
            return together;
        }
    }();
}
Run Code Online (Sandbox Code Playgroud)

文档:

        /* this and that both refer to an array of objects, each containing
           object[by] as one of their fields */
        /*
         N.B. It is the responsibility of the user of this method
         to ensure that the contents of the [by] fields are
         consistent with each other between the two arrays!
        */
        /* select is an array of field names to be included in the resulting
           objects--all other fields will be excluded, or, if the Boolean value
           of omit evaluates to true, then select is an array of field names to
           be excluded from the resulting objects--all others will be included.
        */
Run Code Online (Sandbox Code Playgroud)

  • 好吧,你可以缩小和压缩你的代码,但如果你想让人们阅读它,那就不行了!:-) (3认同)

Ant*_*ton 7

我只是总是使用underscore.js,因为它对数组和"map reduce"有很好的支持,可以解决这个问题.

这里有一个解决你的问题的解决方案(它假设每个用户只有一个问题,因为你的原始帖子建议)

http://jsfiddle.net/x5Z7f/

(打开浏览器控制台查看输出)

    var userProfiles = [{ id:'1', name:'john' }, { id:'2', name:'mary' }];

var questions =[ { id:'1', text:'question john', createdBy:'1' }, { id:'2', text:'question mary', createdBy:'2' }];

var rows = _.map(userProfiles, function(user){ 
    var question = _.find(questions, function(q){ return q.createdBy == user.id });
    user.text = question? question.text:'';
    return user; 
})

_.each(rows, function(row){ console.log(row) });
Run Code Online (Sandbox Code Playgroud)

上面的答案假设您使用id == createdBy作为连接列.


Peb*_*bbl 7

如果是我,我将以下列方式处理:

设置:

var userProfiles = [], questions = [];

userProfiles.push( {id:1, name:'test'} );
userProfiles.push( {id:2, name:'abc'} );
userProfiles.push( {id:3, name:'def'} );
userProfiles.push( {id:4, name:'ghi'} );

questions.push( {id:1, text:'monkey', createdBy:1} );
questions.push( {id:2, text:'Monkey', createdBy:1} );
questions.push( {id:3, text:'big',    createdBy:2} );
questions.push( {id:4, text:'string', createdBy:2} );
questions.push( {id:5, text:'monKey', createdBy:3} );
Run Code Online (Sandbox Code Playgroud)

首先,将创建一个查找对象,其中链接ID用作键

var createObjectLookup = function( arr, key ){
  var i, l, obj, ret = {};
  for ( i=0, l=arr.length; i<l; i++ ) {
    obj = arr[i];
    ret[obj[key]] = obj;
  }
  return ret;
};

var up = createObjectLookup(userProfiles, 'id');
Run Code Online (Sandbox Code Playgroud)

既然你有这个,那么应该很容易逐步解决问题,找到要合并的用户对象:

var i, l, question, user, result = [];
for ( i=0, l=questions.length; i<l; i++ ) {
  if ( (question = questions[i]) && (user = up[question.createdBy]) ) {
    result.push({
      id: question.id,
      text: question.text,
      name: user.name
    });
  }
}
Run Code Online (Sandbox Code Playgroud)

你现在应该拥有你需要的一切 result

console.log(result);
Run Code Online (Sandbox Code Playgroud)


Sna*_*rak 5

在 JavaScript 中执行SQL 连接的简单方法:

let userProfiles = [ { id: 3, name: "Paquito"}, { id: 2, name: "Jaime" } ];
let questions = [ { id: 22, text: "My question", createdBy: 3 }, { id: 44, text: "Other question", createdBy: 5 } ];

let left_join = questions
.map ( q => ({ ...userProfiles.find( u => q.createdBy === u.id ), ...q }) );

document.write("<p>Left join: <br>", JSON.stringify(left_join));

let right_join = userProfiles
.map ( u => ({ ...questions.find( q => q.createdBy === u.id ), ...u }) );;

document.write("</p><p>Right join: <br>", JSON.stringify(right_join));

let inner_join = questions
.filter( q => userProfiles.find( u => q.createdBy === u.id ) )
.map ( q => ({ ...userProfiles.find( u => q.createdBy === u.id ), ...q }) );

document.write("</p><p>Inner join: <br>", JSON.stringify(inner_join));
Run Code Online (Sandbox Code Playgroud)