使用lodash创建多级查找表

时间:2017-06-06 13:17:14

标签: javascript lodash

让我假装一个api正在返回apiResult中的结构,我想创建一个两级索引来查找给定作者对给定文章的评论。

自己的作者,文章和评论的查找表很容易,但我很难对CommentsByArticleAndAuthor - 查找。

mapped结构中,我展示了如何为三个简单的'做出查找索引。部分,以及我希望CommentsByArticleAndAuthor看起来的方式。

如何继续生成此索引?



var apiResult = {
  Authors: [
    {
      Id: "author-id-1",
      Name: "Author One"
    },
    {
      Id: "author-id-2",
      Name: "Author Two"
    }
  ],
  Articles: [
    {
      Id: "article-id-1",
      Title: "This Great Article"
    }
  ],
  Comments: [
    {
       Id: "comment-id-1",
       AuthorId: "author-id-1",
       ArticleId: "article-id-1",
       Comment: "that great, hu?" 
    },
    {
      Id: "comment-id-2",
      AuthorId: "author-id-2",
      ArticleId: "article-id-1",
      Comment: "yes, that great"
    }
  ]
}

var mapped = {
  Authors: _.keyBy(apiResult.Authors, 'Id'),
  Articles: _.keyBy(apiResult.Articles, 'Id'),
  Comments: _.keyBy(apiResult.Comments, 'Id'),
  CommentsByArticleAndAuthor: {
    "article-id-1": {
      "author-id-1": [ "comment-id-1" ],
      "author-id-2": [ "comment-id-2" ]
    }
  }
}

console.log(JSON.stringify(mapped, null, 2));

<script src="https://cdn.jsdelivr.net/lodash/4.17.4/lodash.min.js"></script>
&#13;
&#13;
&#13;

3 个答案:

答案 0 :(得分:1)

您可以迭代注释并构建分层对象结构。

&#13;
&#13;
var apiResult = { Authors: [{ Id: "author-id-1", Name: "Author One" }, { Id: "author-id-2", Name: "Author Two" }], Articles: [{ Id: "article-id-1", Title: "This Great Article" }], Comments: [{ Id: "comment-id-1", AuthorId: "author-id-1", ArticleId: "article-id-1", Comment: "that great, hu?" }, { Id: "comment-id-2", AuthorId: "author-id-2", ArticleId: "article-id-1", Comment: "yes, that great" }] },
    mapped = { CommentsByArticleAndAuthor: {} };

apiResult.Comments.forEach(function (o) {
    var ref = mapped.CommentsByArticleAndAuthor;

    ref[o.ArticleId] = ref[o.ArticleId] || {};
    ref[o.ArticleId][o.AuthorId] = ref[o.ArticleId][o.AuthorId] || [];
    ref[o.ArticleId][o.AuthorId].push(o.Id);
});

console.log(mapped);
&#13;
.as-console-wrapper { max-height: 100% !important; top: 0; }
&#13;
&#13;
&#13;

对于更动态的方法,您可以使用数组作为层次结构,使用对象作为默认值。

&#13;
&#13;
var apiResult = { Authors: [{ Id: "author-id-1", Name: "Author One" }, { Id: "author-id-2", Name: "Author Two" }], Articles: [{ Id: "article-id-1", Title: "This Great Article" }], Comments: [{ Id: "comment-id-1", AuthorId: "author-id-1", ArticleId: "article-id-1", Comment: "that great, hu?" }, { Id: "comment-id-2", AuthorId: "author-id-2", ArticleId: "article-id-1", Comment: "yes, that great" }] },
    mapped = { CommentsByArticleAndAuthor: {} };

apiResult.Comments.forEach(function (o) {
    ['ArticleId', 'AuthorId'].reduce(function (r, k) {
        return r[o[k]] = r[o[k]] || { AuthorId: [] }[k] || {};
    }, mapped.CommentsByArticleAndAuthor).push(o.Id);
});

console.log(mapped);
&#13;
.as-console-wrapper { max-height: 100% !important; top: 0; }
&#13;
&#13;
&#13;

答案 1 :(得分:1)

您可以将apiResult.Comments缩减为所需的结果:

CommentsByArticleAndAuthor: apiResult.Comments.reduce((result, comment) => Object.assign(result, {
  [comment.ArticleId]: Object.assign(result[comment.ArticleId] || {}, {
    [comment.AuthorId]: result[comment.ArticleId] && result[comment.ArticleId][comment.AuthorId]
      ? result[comment.ArticleId][comment.AuthorId].push(comment.Id)
      : [comment.Id]
  })
}), {})

你的例子:

&#13;
&#13;
var apiResult = {
  Authors: [
    {
      Id: "author-id-1",
      Name: "Author One"
    },
    {
      Id: "author-id-2",
      Name: "Author Two"
    }
  ],
  Articles: [
    {
      Id: "article-id-1",
      Title: "This Great Article"
    }
  ],
  Comments: [
    {
       Id: "comment-id-1",
       AuthorId: "author-id-1",
       ArticleId: "article-id-1",
       Comment: "that great, hu?" 
    },
    {
      Id: "comment-id-2",
      AuthorId: "author-id-2",
      ArticleId: "article-id-1",
      Comment: "yes, that great"
    }
  ]
}

var mapped = {
  
  CommentsByArticleAndAuthor: {
    "article-id-1": {
      "author-id-1": [ "comment-id-1" ],
      "author-id-2": [ "comment-id-2" ]
    }
  },
  CommentsByArticleAndAuthor: apiResult.Comments.reduce((result, comment) => Object.assign(result, {
    [comment.ArticleId]: Object.assign(result[comment.ArticleId] || {}, {
      [comment.AuthorId]: result[comment.ArticleId] && result[comment.ArticleId][comment.AuthorId]
        ? result[comment.ArticleId][comment.AuthorId].push(comment.Id)
        : [comment.Id]
    })
  }), {})
}

console.log(mapped);
&#13;
<script src="https://cdn.jsdelivr.net/lodash/4.17.4/lodash.min.js"></script>
&#13;
&#13;
&#13;

答案 2 :(得分:0)

感谢Ninas great answer我想出了这个通用的解决方案:

const apiResult = {
  Authors: [{ Id: "author-id-1", Name: "Author One" }, 
            { Id: "author-id-2", Name: "Author Two" }],
  Articles: [{ Id: "article-id-1", Title: "This Great Article" }],
  Comments: [{ Id: "comment-id-1", AuthorId: "author-id-1", ArticleId: "article-id-1", Comment: "that great, hu?" }, 
             { Id: "comment-id-2", AuthorId: "author-id-2", ArticleId: "article-id-1", Comment: "yes, that great" }]
};
const mapped = {};

const indexBy = function(collection, keys, map = (item) => item) {
  let result = {};

  collection.forEach((item) => {
    keys.reduce((r, k, idx) => {
      let value = _.get(item, k);
      if (idx === keys.length - 1)
        return r[value] = [...r[value] || [], map(item)];

      return r[value] = r[value] || {}
    }, result);
  });

  return result;
}

mapped.CommentsByArticlesAndAuthor = indexBy(apiResult.Comments, ['ArticleId', 'AuthorId'], (item) => item.Id);
mapped.CommentsByAuthorAndArticle = indexBy(apiResult.Comments, ['AuthorId', 'ArticleId'], (item) => item.Id);

console.log(mapped);
.as-console-wrapper {  max-height: 100% !important;  top: 0; }
<script src="https://cdn.jsdelivr.net/lodash/4.17.4/lodash.min.js"></script>

它允许我索引我想要的多个级别,并且它允许我将索引的项目转换为默认,只是存储完整项目。它还利用来自lodash的get来支持嵌套的键值,例如: Author.Id代替AuthorId