用Angular JS基于id合并数组中的多个对象

Merging multiple objects in an array with Angular JS based on id

本文关键字:对象 数组 合并 Angular JS 基于 id      更新时间:2023-09-26

我已经搜索了一段时间,试图找到这个问题的答案,因为我想象有人遇到这种情况。请指出我在正确的方向,如果答案已经在那里。我有一个包含图书的对象数组。这些书存储在数据库中,可以有一个或多个作者。对于writers表中的每个writer,都有一个用于book_id(FK)、writer_name和review_id(FK)的列。因此,如果一本书上有多个作者,则每个作者都有一行包含除了名字以外的相同信息。下面是一个来自DB的JSON响应示例:

var data = [
{"id": 1, "writer" : "John Joseph Doe", "review_id" : 4},
{"id": 1, "writer" : "Daniel Smith", "review_id" : 4},
{"id": 1, "writer" : "Thomas Edward Jones", "review_id" : 4},
{"id": 2, "writer" : "Erin Davis", "review_id" : 5},
{"id": 2, "writer" : "Jill Steinberg", "review_id" : 5},
{"id": 2, "writer" : "Laurie Beth Jennings", "review_id" : 5},
{"id": 3, "writer" : "Emma Jean Williams", "review_id" : 3},
{"id": 3, "writer" : "Mary Joe Williams", "review_id" : 3},
{"id": 3, "writer" : "Helen Andrews", "review_id" : 3},
{"id": 3, "writer" : "Samantha Jones", "review_id" : 3}
];

我想过滤这个数组,这样我最终得到3个对象,每本书一个,没有重复值,每个作者连接为逗号分隔的字符串。像这样:

var data = [
{"id": 1, "writer" : "John Joseph Doe, Daniel Smith, Thomas Edward Jones", "review_id" : 4},
{"id": 2, "writer" : "Erin Davis, Jill Steinberg, Laurie Beth Jennings", "review_id" : 5},
{"id": 3, "writer" : "Emma Jean Williams, Mary Joe Williams, Helen Andrews, Samantha Jones", "review_id" : 3}
];

因此,我尝试创建一个函数,该函数使用for循环和'first'变量来根据它的id跟踪我所在的对象。我的逻辑是将每个写入器添加到具有唯一id的每个对象的第一个实例中,然后在添加写入器后删除具有相同id的所有后续对象。因此,每个图书id只剩下一个对象和一个用逗号分隔的作者连接字符串。结果很接近我想要的,但是我做得不对。我不确定是否从数组中删除对象是我的问题,或者我是否采取了完全错误的方法。这里有一个工作小提琴:http://jsfiddle.net/mlabita37/Lvc0u55v/8987/。表上方有一个文本区,我使用它作为"控制台"来显示最终数据数组,只需展开即可看到完整的结果。

下面是该代码的一些片段:

HTML:

<div ng-controller="BookCtrl">
<table class="table table-striped">
 <thead>
  <tr>
    <th>Book ID</th>
    <th>Writer</th>
    <th>Review ID</th>
  </tr>
</thead>
<tbody>
  <tr ng-repeat="book in books">
    <td>{{ book.id }}</td>
    <td>{{ book.writer }}</td>
    <td>{{ book.review_id }}</td>
    <textarea>{{ books }}</textarea>
  </tr>
</tbody>

控制器:

var data = [
{"id": 1, "writer" : "John Joseph Doe", "review_id" : 4},
{"id": 1, "writer" : "Daniel Smith", "review_id" : 4},
{"id": 1, "writer" : "Thomas Edward Jones", "review_id" : 4},
{"id": 2, "writer" : "Erin Davis", "review_id" : 5},
{"id": 2, "writer" : "Jill Steinberg", "review_id" : 5},
{"id": 2, "writer" : "Laurie Beth Jennings", "review_id" : 5},
{"id": 3, "writer" : "Emma Jean Williams", "review_id" : 3},
{"id": 3, "writer" : "Mary Joe Williams", "review_id" : 3},
{"id": 3, "writer" : "Helen Andrews", "review_id" : 3},
{"id": 3, "writer" : "Samantha Jones", "review_id" : 3}
];
combine = function(data){
  var first = data[0];
  for(var i = 1; i < data.length; i++){
    if(data[i].id === first.id){
      first.writer = first.writer + ", " + data[i].writer;
      data.splice(i, 1);
    }
    if(data[i].id !== first.id){
      first = data[i];
      var j = i+1;
      if(data[j].id === first.id){
        first.writer = first.writer + ", " + data[j].writer;
        data.splice(j, 1);
      }
   }
 }  
};
combine(data);
$scope.books = data;

我的另一个想法是使用过滤器,但我没有太多的经验,不知道这是否是一个更好的方法。我也不确定这是否可以/应该在我的MySQL查询中处理,或者在后端PHP中处理。我的印象是这个问题最好在前端处理。

var data = [
{"id": 1, "writer" : "John Joseph Doe", "review_id" : 4},
{"id": 1, "writer" : "Daniel Smith", "review_id" : 4},
{"id": 1, "writer" : "Thomas Edward Jones", "review_id" : 4},
{"id": 2, "writer" : "Erin Davis", "review_id" : 5},
{"id": 2, "writer" : "Jill Steinberg", "review_id" : 5},
{"id": 2, "writer" : "Laurie Beth Jennings", "review_id" : 5},
{"id": 3, "writer" : "Emma Jean Williams", "review_id" : 3},
{"id": 3, "writer" : "Mary Joe Williams", "review_id" : 3},
{"id": 3, "writer" : "Helen Andrews", "review_id" : 3},
{"id": 3, "writer" : "Samantha Jones", "review_id" : 3}
];
function existsAt(array, key, value) {
    for (var i = 0; i < array.length; i++) {
        if (array[i][key] == value) {
            return i;
        }
    }
    return false;
}
var _data = [];
_data.push(data[0]);
for (var i = 1; i < data.length; i++) {
    var alreadyExistsAt = existsAt(_data, 'id', data[i].id);
    if (alreadyExistsAt !== false) {
        _data[alreadyExistsAt].writer += ', ' + data[i].writer;
    } else {
        _data.push(data[i]);
    }
}
document.body.innerHTML = JSON.stringify(_data);

我希望代码中的变量名能够很好地解释。

  • 外部forEach回路依次从完整的评审列表中选择每个评审(即currReview)。
  • 内部的forEach循环遍历每个预先存在的连接的书评对象(即prevReviewsForThisBook…一开始是空的)。这个内循环确定当前的评论是否针对已经至少有一个评论的书:
    • 如果是,则更新该预先存在的审阅对象的writer属性以包含新的编写器。
    • 如果不是,那么整个当前评论对象将被推送到已有的书评对象数组中。

请注意,您的id(可能是您的book_id)始终与您的review_id相同。这是你想要的吗?似乎其中一个是多余的/不必要的,或者其中一个是计算/确定不正确的。后者似乎是可能的。例如,John Joseph Doe所做的review的review_id和Daniel Smith所做的review的review_id都应该具有相同的值4吗?我认为这些是单独的评论,因此应该有不同的review_id值,即使它们都有相同的id/book_id值(因为这两个人都评论了同一本书)。这个问题不是回答这个问题的核心,但您可能需要重新考虑。

var data = allReviews = [
  {"id": 1, "writer" : "John Joseph Doe", "review_id" : 4},
  {"id": 1, "writer" : "Daniel Smith", "review_id" : 4},
  {"id": 1, "writer" : "Thomas Edward Jones", "review_id" : 4},
  {"id": 2, "writer" : "Erin Davis", "review_id" : 5},
  {"id": 2, "writer" : "Jill Steinberg", "review_id" : 5},
  {"id": 2, "writer" : "Laurie Beth Jennings", "review_id" : 5},
  {"id": 3, "writer" : "Emma Jean Williams", "review_id" : 3},
  {"id": 3, "writer" : "Mary Joe Williams", "review_id" : 3},
  {"id": 3, "writer" : "Helen Andrews", "review_id" : 3},
  {"id": 3, "writer" : "Samantha Jones", "review_id" : 3}
];
var reviewsByBook = [];
allReviews.forEach(function(currReview) {
  var currReviewIsForNewBook = true;
  reviewsByBook.forEach(function(prevReviewsForThisBook) {
    if (currReview.id === prevReviewsForThisBook.id) {
      prevReviewsForThisBook.writer += ", " + currReview.writer;
      currReviewIsForNewBook = false;
    }
  });
  if (currReviewIsForNewBook) {
    reviewsByBook.push(currReview);
 }
});
console.log(reviewsByBook);