Existe alguma maneira que eu possa “juntar” o conteúdo de dois arrays javascript muito parecido com eu faria uma junit no SQL

Eu tenho dois arrays: Question and UserProfile

  • O userProfiles : [] contém objects { id, name }
  • O array questions : [] contém objects { id, text, createdBy }

O inteiro createdBy em perguntas é sempre um dos valores de id em userProfiles .

Existe uma maneira que eu poderia “juntar” as matrizes da mesma maneira como eu iria juntar duas tabelas SQL se eu estivesse usando um database.

O que eu preciso como resultado final é um array que contém

 { id, text, name } 

Esta parece ser uma questão de propósito geral importante, e embora haja muitas respostas, algumas têm um comportamento limítrofe como modificar os dados existentes, resolvendo um problema completamente diferente do que o problema em questão, usando até 93.057 bytes de JavaScript (para não mencionar produzindo o resultado errado), produzindo aninhamento adicional excessivamente complexo de estruturas de dados, exigindo muito código em cada invocação e, mais seriamente, não sendo uma solução independente para o importante problema de propósito mais geral no cerne dessa questão.

Então, para melhor ou para pior, eu escrevi um shim que estende o object JavaScript Array com um método .joinWith destinado a ser usado para unir this matriz de objects com that matriz de objects, by um campo de indexação comum. É possível select uma lista de campos desejados na saída (boa para mesclar matrizes de objects com muitos campos quando apenas alguns são desejados) ou omit uma lista de campos na saída (boa para mesclar matrizes de objects quando a maioria dos campos é desejada mas alguns não são).

O shim code não foi feito para parecer bonito, então será no final, com o exemplo de como usá-lo para o tipo particular de dados do OP em primeiro lugar:

 /* 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, ie, * does exactly what you need. I haven't seen your actual data, so it's * possible that your IDs are are not in common, (ie, 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. * */ 

O código a seguir é para demonstração:

 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"}]* 

Há outras coisas legais que essa solução faz (uma delas é preservar a capacidade de acessar os dados resultantes por sua chave de indexação, apesar de retornar um array).

Apreciar!

 /* 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; } }(); } 

Documentação:

  /* this and that both refer to an array of objects, each containing object[by] as one of their fields */ /* NB 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. */ 

Eu acho que o que você quer é uma junit interna , que é simples o suficiente para implementar em 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), []); 

Para fins de demonstração, usaremos o seguinte dataset (obrigado @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}, ]; 

É assim que você usaria:

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

Em termos SQL, isso seria semelhante a:

 SELECT questions.id, questions.text, userProfiles.name FROM userProfiles INNER JOIN questions ON questions.createdBy = userProfiles.id; 

Juntando tudo:

 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(result); 

Eu quase sempre uso o underscore.js porque ele tem um suporte tão bom para arrays e “map reduce” com os quais este problema pode ser resolvido.

aqui está um violino com uma solução para sua pergunta (presume-se que haja apenas uma pergunta por usuário como sua postagem original sugere)

http://jsfiddle.net/x5Z7f/

(abra o console do navegador para ver a saída)

  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) }); 

a resposta acima assume que você está usando id == createdBy como a coluna de união.

Se fosse eu, abordaria isso da seguinte maneira:

O set-up:

 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} ); 

Primeiro, seria criar um object de pesquisa, onde o id de vinculação é usado como chave

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

Agora que você tem isso, deve ser fácil percorrer as perguntas e encontrar seu object de usuário para mesclar:

 var i, l, question, user, result = []; for ( i=0, l=questions.length; i 

Agora você deve ter tudo que precisa no result

 console.log(result); 

tudo que você quer é o ResultArray calculado abaixo:

  var userProfiles1= new Array(1, "ashok"); var userProfiles2= new Array(2, "amit"); var userProfiles3= new Array(3, "rajeev"); var UArray = new Array(userProfiles1, userProfiles2, userProfiles3); var questions1= new Array(1, "text1", 2); var questions2= new Array(2, "text2", 2); var questions3= new Array(3, "text3", 1); var questions4= new Array(4, "text4", 2); var questions5= new Array(5, "text5", 3); var questions6= new Array(6, "text6", 3); var QArray = new Array(questions1, questions2, questions3, questions4, questions5, questions6); var ResultArray = new Array(); for (var i=0; i") } 

demo: http://jsfiddle.net/VqmVv/

Esta é a minha tentativa de fazer uma solução genérica. Estou usando o Array.map e os methods Array.index aqui:

 var arr1 = [ {id: 1, text:"hello", oid:2}, {id: 2, text:"juhu", oid:3}, {id: 3, text:"wohoo", oid:4}, {id: 4, text:"yeehaw", oid:1} ]; var arr2 = [ {id: 1, name:"yoda"}, {id: 2, name:"herbert"}, {id: 3, name:"john"}, {id: 4, name:"walter"}, {id: 5, name:"clint"} ]; function merge(arr1, arr2, prop1, prop2) { return arr1.map(function(item){ var p = item[prop1]; el = arr2.filter(function(item) { return item[prop2] === p; }); if (el.length === 0) { return null; } var res = {}; for (var i in item) { if (i !== prop1) { res[i] = item[i]; } } for (var i in el[0]) { if (i !== prop2) { res[i] = el[0][i]; } } return res; }).filter(function(el){ return el !== null; }); } var res = merge(arr1, arr2, "oid", "id"); console.log(res); 

Então, basicamente, você pode definir duas matrizes e uma propriedade para cada matriz, de modo que prop1 seja substituído por todas as propriedades de um item na matriz2 cuja prop2 seja igual a prop1.

O resultado neste caso seria:

 var res = [ {id: 1, text:"hello", name:"herbert"}, {id: 2, text:"juhu", name:"john"}, {id: 3, text:"wohoo", name:"walter"}, {id: 4, text:"yeehaw", name:"yoda"} ]; 

Observe que, se houver mais de uma correspondência, o primeiro item será usado e, se não houver correspondência, o object será removido da matriz resultante.

FIDDLE

Só queria compartilhar algum código genérico:

 // Create a cartesian product of the arguments. // product([1,2],['a','b'],['X']) => [[1,"a","X"],[1,"b","X"],[2,"a","X"],[2,"b","X"]] // Accepts any number of arguments. product = function() { if(!arguments.length) return [[]]; var p = product.apply(null, [].slice.call(arguments, 1)); return arguments[0].reduce(function(r, x) { return p.reduce(function(r, y) { return r.concat([[x].concat(y)]); }, r); }, []); } 

Seu problema:

 result = product(userProfiles, questions).filter(function(row) { return row[0].id == row[1].createdBy; }).map(function(row) { return { userName: row[0].name, question: row[1].text } }) 

Você pode fazer isso usando reduzir e mapear .

Primeiro, crie um mapeamento de IDs para nomes:

 var id2name = userProfiles.reduce(function(id2name, profile){ id2name[profile.id] = profile.name; return id2name; }, {}); 

Segundo, crie uma nova matriz de perguntas, mas com o nome do usuário que criou a pergunta no lugar de seu ID:

 var qs = questions.map(function(q){ q.createdByName = id2name[q.createdBy]; delete q.createdBy; return q; }); 

Não conheço nenhuma function incorporada que permita fazer isso.

Você pode programar sua própria function, algo semelhante a este jsFiddle :

 var userProfiles = [{id:1, name:'name1'},{id:2,name:'name2'}]; var questions = [ {id:1, text:'text1', createdBy:'foo'}, {id:1, text:'text2', createdBy:'bar'}, {id:2, text:'text3', createdBy:'foo'}]; merged = mergeMyArrays(userProfiles,questions); console.log(merged); /** * This will give you an array like this: * [{id:1, name:name1, text:text1}, {...] * params : 2 arrays to merge by id */ function mergeMyArrays(u,q){ var ret = []; for(var i = 0, l = u.length; i < l; i++){ var curU = u[i]; for(var j = 0, m = q.length; j 

Ou se você quiser um melhor "join" (SQL-y):

 var userProfiles = [{id:1, name:'name1'},{id:2,name:'name2'}]; var questions = [ {id:1, text:'text1', createdBy:'foo'}, {id:1, text:'text2', createdBy:'bar'}, {id:2, text:'text3', createdBy:'foo'}]; merged = mergeMyArrays(userProfiles,questions); console.log(merged); /** * This will give you an array like this: * [{id:1, name:name1, questions:[{...}]] * params : 2 arrays to merge by id */ function mergeMyArrays(u,q){ var ret = []; for(var i = 0, l = u.length; i < l; i++){ var curU = u[i], curId = curU.id, tmpObj = {id:curId, name:curU.name, questions:[]}; for(var j = 0, m = q.length; j 

Como neste jsFiddle

Isso é feito facilmente com o StrelkiJS

 var userProfiles = new StrelkiJS.IndexedArray(); userProfiles.loadArray([ {id: 1, name: "Ashok"}, {id: 2, name: "Amit"}, {id: 3, name: "Rajeev"} ]); var questions = new StrelkiJS.IndexedArray(); questions.loadArray([ {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} ]); var res=questions.query([{ from_col: "createdBy", to_table: userProfiles, to_col: "id", type: "outer" }]); 

O resultado será:

 [ [ {"id":1,"text":"text1","createdBy":2}, {"id":2,"name":"Amit"} ], [ {"id":2,"text":"text2","createdBy":2}, {"id":2,"name":"Amit"} ], [ {"id":3,"text":"text3","createdBy":1}, {"id":1,"name":"Ashok"} ], [ {"id":4,"text":"text4","createdBy":2}, {"id":2,"name":"Amit"} ], [ {"id":5,"text":"text5","createdBy":3}, {"id":3,"name":"Rajeev"} ], [ {"id":6,"text":"text6","createdBy":3}, {"id":3,"name":"Rajeev"} ] ] 

A segunda solução O (m + n) de Aadit me pareceu boa, concisa, funcional e eficiente. No entanto, ele não implementou a duplicação de registros quando várias chaves correspondem ao que eu precisava para o meu caso de uso, então escrevi essa pequena variação:

 function equijoin(arrL,arrR,keyL,keyR=keyL){ const idx = arrL.reduce( (idx, objL) => objL[keyL] === undefined ?idx :idx.set(objL[keyL], [...(idx.get(objL[keyL])||[]), ...[objL]]) ,new Map ) const matches = arrR .map(objR => objR[keyR] === undefined ? [] : idx.get(objR[keyR]) .map(objL => ({l:objL, r:objR})) .reduce((a,b)=>a.concat(b),[]) ) .reduce((a,b)=>a.concat(b),[]) return matches } 

(Contrived) Exemplo:

 var players =[ {id:"Alice", team:"Red"}, {id:"Bob", team:"Red"}, {id:"Clair", team:"Blue"}, {id:"Dave"}, {id:"Elliot"} ]; 

equijoin(players,players,"team")

 [ {l:{id:"Alice",team:"Red"},r:{id:"Alice",team:"Red"}}, {l:{id:"Bob",team:"Red"},r:{id:"Alice",team:"Red"}}, {l:{id:"Alice",team:"Red"},r:{id:"Bob",team:"Red"}}, {l:{id:"Bob",team:"Red"},r:{id:"Bob",team:"Red"}}, {l:{id:"Clair",team:"Blue"},r:{id:"Clair",team:"Blue"}} ] 

Você pode usar primeiro jQuery.merge () e depois jQuery.unique () para conseguir isso. merge () adicionará todos os itens em um array e unique () removerá os duplicados desse array.

http://api.jquery.com/jQuery.merge/

http://api.jquery.com/jQuery.unique/