Rethinkdb Массив объекта для объекта массива

Я пытаюсь работать с Rethinkdb на карте, уменьшая что-то вроде: [{"a":1, "b":2}, {"a":3}, {"c":4}] до {"a":[1,3], "b":[2], "c":[4]}.

Я уже консультировался с Javascript: как преобразовать массив объектов в объект с отсортированными уникальными массивами? но операторы на самом деле не работают в ReQL, вот пример:

r.expr([{"a":1, "b":2}, {"a":3}, {"c":4}]).do(function(user){
  var c1 = {};
    var keys = ["a", "b", "c"];      

    user.map(function(itm){
      keys.map(function(p){
        if(!c1[p]){
          c1[p] = [];
        };
        c1[p] = r.expr(c1[p]).setInsert(itm(p).default("NIL"));
      });
      return 1
    });
  return c1
});

но это выходит из строя на itm(p) с ошибкой: RqlCompileError: Variable name not found in:

RqlCompileError: Variable name not found in:
r([{a: 1, b: 2}, {a: 3}, {c: 4}]).do(function(var_136) { return {a: r([]).setInsert(var_137("a").default("NIL")), b: r([]).setInsert(var_137("b").default("NIL")), c: r([]).setInsert(var_137("c").default("NIL"))}; })
                                                                                    ^^^^^^^                                                                                                                            

потому что rethinkdb присваивает идентификатор переменной (в данном случае 137) элементу (p), который не объявлен заранее.

Любые идеи, как я могу это сделать?

Спасибо


person Xrave    schedule 20.03.2015    source источник
comment
Кажется, вы используете массив ['a','b','c'] для указания порядка ключей в возвращаемом объекте, но объекты javascript не имеют порядка, ключи могут быть возвращены в любой последовательности независимо от порядка их добавления (или алфавитного или числового порядка ). На практике большинство браузеров будут возвращать свойства объекта в том порядке, в котором они были добавлены, но это не гарантируется и в некоторых случаях несовместимо в некоторых браузерах, и на него нельзя полагаться.   -  person RobG    schedule 21.03.2015
comment
@RobG Спасибо. Я не думаю, что меня интересует порядок, просто механизм преобразования [{}] в {[]}. Я немного переработал код и обнаружил, что похоже на ошибку в ReQL, и отправил проблема здесь   -  person Xrave    schedule 21.03.2015
comment
Круто, просто чтобы вы знали… :-)   -  person RobG    schedule 21.03.2015


Ответы (2)


Адаптация ответа от danielmewes на github

var input = r.expr([{"a":1}, {"a":3}, {b: 2}, {"a":4}, {b: 3}]);
var keys = input.map(function(x){return x.keys()})
  .reduce(function(l, r){return l.setUnion(r)});
  .map(function(key){ return [key, input(key)]})
  .coerceTo('object');

Поскольку здесь используются некоторые продвинутые методы ReQL, вот как это происходит:

  1. Сначала мы получаем ключи каждого объекта в массиве. В результате получается массив массивов
  2. Далее мы сводим, это делает попарное объединение элементов из предыдущего массива массивов. В итоге мы получаем дедуплицированный список всех ключей в любом из объектов в исходном массиве.
  3. Next, for each key, we create a two element array where the first element is the key, and the second element is an array of all of the values in the original input with that key.
    • this works because () in reql is more powerful than regular javascript [] brackets. It will collect the values of the field with that key.
    • результатом этого шага является массив массивов, таких как [['a', [1,2]], ['b', [3,4]]
  4. Наконец, мы принуждаем к объекту. ReQL может преобразовать массивы типа [['a', 1], ['b', 2]] в объект {a: 1, b: 2}, что здесь очень полезно.
person deontologician    schedule 21.03.2015

Вот ответ (или один из способов сделать это), если кому интересно...

r.expr([{"a":1, "b":2}, {"a":3}, {"c":4}]).do( r.js("(function(input){ 
   var keys=['a','b','c'], output={}; 
   input.forEach(function(e){ 
        for(var p in e){ 
              output[p] = (output[p] || []).concat(e[p]);  
   } }); 
   return output; })"
))
person Xrave    schedule 21.03.2015