1

我希望外面有人可以来拯救我。出于某种原因,我无法理解 node.js 中的递归。如果有另一种方法,它甚至不必是递归。

我正在使用 redis 集合来存储集合中的层次结构:

SADD parents.<name> <parent1> <parent2>

然后, parent1 和 parent2 也将有条目,并且向上。我想将其转换为 JSON 对象数组。

JSON 将如下所示:

[
 {
     label: <name>,
     parents: [
         { label: <parent1>,
           parents: [ {label: <grandparent1>}] },
         { label: <parent2> }
     ]
 }
]

等等等等。这应该能够适用于任何深度,尽管平均而言它只有 4-6 个节点深。

这是我一直在玩的一些代码,它让我进入了第一级:

var redis = require('node-redis');
var r_client = redis.createClient();

function get_parents (name, current, cb) {

        var output = new Array;
        output.push( { label: name, parents: [] } );

        r_client.smembers('parents.' + name, function(err, reply) {
            for (var i = 0; i < reply.length; i++)
            {
                var name = reply[i].toString('utf8');
                output[i].parents.push({label: name, parents: [] });
             }
             cb (output);
        });
}

get_parents( 'bob', function(out) {console.log('Final output: ' + JSON.stringify( out ))} );

我基本上想这样做:

  • 从根节点开始。打电话给redis,找父母。
  • 为根节点构建对象。
  • 调用相同的函数来构建其他对象。
  • 当对 redis 的调用开始返回 null 时,调用将返回并开始组合对象。

任何帮助将不胜感激。

编辑:更新 get_parents (仍然不起作用):

function get_parents (name, cb) {
    r_client.smembers('parents.' + name, function(err, reply) {
    for (var i = 0; i < reply.length; i++)
    {
      var name = reply[i].toString('utf8');
      output.push( { label: name, parents: [] } );
      output[i].parents = get_parents (output[i].parents.name, cb);
    }

    cb (output);
  });
}

编辑:我决定使用 Promises,所以我选择了这个选项。感谢所有的帮助!

4

2 回答 2

6

TL;DR:我制作了一个 NodeJS 模块(RedisTree)和一篇解释实现的博客文章。


上面的代码是初始实现

不错的挑战!这是一个符合您要求的实现,我添加了一个“.save(tree, f)”方法,它使用lodashasync,当然还有node_redis

var sampleTree = [{
  label: 'label1',
  parents: [{
    label: 'label2',
    parents: [{
      label: 'label4',
      parents: []
    }]
  }, {
    label: 'label3',
    parents: []
  }]
}];

// Note: it's required to use a set in order to retrieve data
// SET:
// sadd label1 label2 label3
// sadd label2 label4
var _     = require('lodash');
var async = require('async');
var redis = require('redis').createClient();


function RedisTree(){}

/**
 * Iteratively & asynchronously retrieve an item from Redis
 * @param  {String} label label name (set name)
 * @param  {Function} f(err, arrayOfItems)
 */
RedisTree.prototype._getItem = function(label, f) {
  var parent = _.isArray(_.last(arguments)) ? _.last(arguments) : [];

  this.members(label, function(err, cards){
    var item = {
      label: this.label(label),
      parents: []
    };
    parent.push(item);

    if(cards.length === 0){return f(null, parent);}

    async.map(cards, _.partialRight(this._getItem.bind(this), item.parents), function(e){f(e, parent);});
  }.bind(this));
};

RedisTree.prototype._saveItem = function(item, f) {
  var labels = _.pluck(item.parents, 'label');
  if(labels.length === 0){return f();}

  redis.sadd(item.label, labels, function(err){
    if(err){return f(err);}
    this._saveItems(item.parents, f);
  }.bind(this));
};

/**
 *
 * @param  {Array} arrToSave array of items
 * @param  {Function} f(err)
 */
RedisTree.prototype._saveItems = function(arrToSave, f) {
  async.forEach(arrToSave, this._saveItem.bind(this), f);
};

/**
 * Retrieve a name from the label
 * Can be overridden to provide namespace support
 * e.g. return label.substring(2);
 * @return {[type]} [description]
 */
RedisTree.prototype.label = function(label){return label;};

/**
 * Retrieve every members of the `label` set from redis
 * Can be overridden to provide namespace support
 * e.g. redis.smembers('ns.'+label, f);
 * @param {[type]} label [description]
 * @param {[type]} f     [description]
 */
RedisTree.prototype.members = function(label, f) {redis.smembers(label, f);};

/**
 * Load a tree from Redis
 * @param  {String} startLabel Were to start
 * @param  {Function} f(err, tree)
 */
RedisTree.prototype.load = function(startLabel, f){this._getItem(startLabel, f);};

/**
 * Save a tree from Redis
 * @param  {Array} treeArray
 * @param  {Function} f(err, tree)
 */
RedisTree.prototype.save = function(treeArray, f){this._saveItems(treeArray, f);};

以下是如何使用它:

var t = new RedisTree();

// Save the sampleTree
t.save(sampleTree, function(err){
  // or ... retrieve it starting at "label1"
  t.load('label1', function(err, tree){
    console.log("sampleTree === ", JSON.stringify(tree, null, 1));
  });
});
于 2013-09-17T16:09:09.690 回答
0

您确定不能将其作为 JSON 存储在磁盘或 MongoDB 中吗?另外,为什么您想要的结果列出父母而不是孩子?

于 2013-09-17T17:42:45.583 回答