Подтвердить что ты не робот

Есть ли способ, которым я могу "присоединиться" к содержимому двух массивов javascript, так же, как я бы сделал присоединение к SQL

У меня есть два массива: Question и UserProfile

  • В массиве userProfiles: [] содержатся объекты { id, name }
  • Массив questions: [] содержит объекты { id, text, createdBy }

Целое число createdBy в вопросах всегда является одним из значений id в userProfiles.

Есть ли способ, с помощью которого я мог бы "присоединить" массивы так же, как я бы объединил две таблицы SQL, если бы я использовал базу данных.

Что мне нужно в качестве конечного результата, это массив, содержащий

{ id, text, name }
4b9b3361

Ответ 1

Это, по-видимому, важный вопрос общего назначения, и хотя есть много ответов, некоторые из них имеют пограничное поведение, такое как изменение существующих данных, решение совершенно другой проблемы, чем проблема, использующая до 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.
 *
 */

Следующий код предназначен для демонстрации:

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

Есть и другие приятные вещи, которые это решение делает (один из них сохраняет возможность доступа к результирующим данным с помощью ключа индексирования, несмотря на возвращение массива).

Наслаждайтесь!

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

Документация:

        /* 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.
        */

Ответ 2

Я думаю, что вы хотите внутреннее соединение, которое достаточно простое реализовать в 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), []);

В целях демонстрации мы будем использовать следующий набор данных (спасибо @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},
];

Вот как вы его используете:

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

В терминах SQL это будет похоже на:

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

Объединяя все это:

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

Ответ 3

я просто всегда использую underscore.js, потому что он имеет такую ​​хорошую поддержку массивов и "сокращение карты", с которыми эта проблема может быть решена с помощью.

вот скрипка с решением для вашего вопроса (он предполагает, что есть только один вопрос для пользователя, как предполагает ваш исходный пост)

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

приведенный выше ответ предполагает, что вы используете id == createdBy в качестве столбца соединения.

Ответ 4

Если бы это был я, я бы подошел к этому следующим образом:

Настройка:

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

Во-первых, было бы создание объекта поиска, где идентификатор связывания используется как ключ

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

Теперь, когда у вас есть это, вы должны легко пройти через вопросы и найти свой пользовательский объект для объединения:

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

Теперь вы должны иметь все, что вам нужно, в result

console.log(result);

Ответ 5

all u want - это ResultArray, рассчитанный ниже:

    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<UArray.length; i++)
    {
        var uid = UArray[i][0];
        var name = UArray[i][1];

        for(var j=0; j<QArray.length; j++)
        {
            if(uid == QArray[j][2])
            {
                 var qid = QArray[j][0]
                 var text = QArray[j][1];

                 ResultArray.push(qid +"," + text +","+ name)
            }
        }    
    }

for(var i=0; i<ResultArray.length; i++)
    {
        document.write(ResultArray[i] + "<br>")
    }

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

Ответ 6

Это моя попытка сделать какое-то общее решение. Я использую методы Array.map и Array.index здесь:

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

Таким образом, вы можете определить два массива и одно свойство для каждого массива, так что prop1 будет заменен всеми свойствами элемента в массиве2, prop2 которого равен prop1.

Результатом в этом случае будет:

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

Обратите внимание, что если есть более одного совпадения, будет использоваться первый элемент, и если совпадения нет, объект будет удален из результирующего массива.

FIDDLE

Ответ 7

Просто хотел поделиться некоторым общим кодом:

// 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);
    }, []);
}

Ваша проблема:

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

Ответ 8

Я не знаю встроенной функции, позволяющей сделать это.

Вы можете запрограммировать свою собственную функцию, что-то похожее на this 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<m; j++){
                if(q[j].id == curU.id){
                    ret.push({
                        id: curU.id,
                        name: curU.name,
                        text: q[j].text
                    });
                }
            }
        }
        return ret;
    }

Или, если вы хотите улучшить "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<m; j++){
            if(q[j].id == curId){
                tmpObj.questions.push({
                    text: q[j].text,
                    createdBy: q[j].createdBy
                });
            }
        }
        ret.push(tmpObj);
    }
    return ret;
}

Как в this jsFiddle

Ответ 9

Вы можете сделать это, используя reduce и map.

Сначала создайте сопоставление от идентификаторов к именам:

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

Во-вторых, создайте новый массив вопросов, но с именем пользователя, который создал вопрос вместо своего идентификатора:

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

Ответ 10

Это легко сделать с помощью 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"
}]);

Результат будет:

[
 [
  {"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"}
 ]
]

Ответ 11

Вы можете использовать сначала jQuery.merge(), а затем jQuery.unique(), чтобы достичь этого. merge() добавит все элементы в один массив, а unique() удалит дубликаты из этого массива.

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

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