Javascript 组合对象数组及其属性。合并后删除对象

Javascript 组合对象数组及其属性。合并后删除对象,javascript,Javascript,我需要将这些对象合并在一起。资源属性决定是否可以合并对象。要确定小时属性值的去向,请使用可计费属性。我希望数组看起来像这样 members = [{billable: true, hours: 15, name: "Joe Smith", resource: "00530000003mgYGAAY", totalBillableHours: 20, totalHours: 25, totalNonBillableHours: 5}, {billable: false, hours: 5, nam

我需要将这些对象合并在一起。资源属性决定是否可以合并对象。要确定小时属性值的去向,请使用可计费属性。我希望数组看起来像这样

members = [{billable: true, hours: 15, name: "Joe Smith", resource: "00530000003mgYGAAY", totalBillableHours: 20, totalHours: 25, totalNonBillableHours: 5},
{billable: false, hours: 5, name: "Jan Smith", resource: "00530000003mgYTAAY", totalBillableHours: 14, totalHours: 19, totalNonBillableHours: 10}]
最终结果的billable和hours属性的状态是不相关的

这是密码。我似乎不明白为什么它不起作用


当您在数组上迭代时从数组中删除项时,它会变得非常棘手

我在这里以更实用的方式重写了您的解决方案:

结果输出就是您要查找的内容:

Array[2]
    0 : Object
        billable : true
        hours : 15
        name : "Joe Smith"
        resource : "00530000003mgYGAAY"
        totalBillableHours : 20
        totalHours : 25
        totalNonBillableHours : 5
        __proto__ : Object
    1 : Object
        billable : false
        hours : 5
        name : "Jan Smith"
        resource : "00530000003mgYTAAY"
        totalBillableHours : 14
        totalHours : 19
        totalNonBillableHours : 5
        __proto__ : Object
    length : 2
    __proto__ : Array[0]

问题是您依赖于每个记录的相邻记录。相反,您可以根据每个成员的资源对其进行计数。由于资源是唯一的,所以可以将其用作保持计数的对象的属性—将其视为一个键。然后可以将每个记录中的小时数添加到相应的对象中

以下是我的尝试:


一种技术是在每个成员上循环,找到第一个包含该资源的成员,更新其中的总数,然后进行筛选,以便只保留第一次出现的成员

members.filter(member => {
  const first = members.find(m => m.resource === member.resource);

  if (member.billable) first.totalBillableHours += member.hours;
  else first.totalNonBillableHours += member.hours;
  first.totalHours += member.hours.

  return first === member;
});
更传统的方法是按资源对对象进行分组,为每个资源创建一个对象数组,然后将其转换为所需的输出,如中所示

totals(groupBy(members, 'resource'))
groupBy将被定义为生产某种形式的产品:

{
  resource1: [obj, obj],
  resource2: [obj, obj]
}
首先计算总数,那将是

function totals(groups) {
  const hours    = m => m.hours;
  const billable = m => m.billable;
  const not      = f => x => !f(x);

  return Object.keys(groups).map(resource => {
    const members          = groups[resource];
    const totalHours       = sum(members.map(hours));
    const billableHours    = sum(members.filter(billable).map(hours));
    const nonBillableHours = sum(members.filter(not(billable)).map(hours));

    return {resource, totalHours, billableHours, nonBillableHours};
  });
}
总和可以写成

const sum = arr => arr.reduce((a, b) => a + b, 0);
groupBy有许多实现,包括由下划线等库提供的实现。下面是一个真正简单的版本:

function groupBy(arr, prop) {
  return arr.reduce((result, obj) {
    const key = obj[prop];
    if (!result[key]) result[key] = [];
    result[key].push(obj);
    return result;
  }, {});
}

以下是使用库免责声明的版本:我是作者之一:

const process = pipe(
  groupBy(prop('resource')),
  values,
  map(group => reduce((totals, member) => ({
    name: member.name,
    resource: member.resource,
    totalHours: totals.totalHours + member.hours,
    totalBillableHours: totals.totalBillableHours + 
            (member.billable ? member.hours : 0),
    totalNonBillableHours: totals.totalNonBillableHours + 
            (member.billable ? 0 : member.hours)
  }), head(group), group))
);
有了这个,

process(members)
屈服

[
  {
    name: "Joe Smith",
    resource: "00530000003mgYGAAY",
    totalBillableHours: 20,
    totalHours: 25,
    totalNonBillableHours: 5
  },
  {
    name: "Jam Smith",
    resource: "00530000003mgYTAAY",
    totalBillableHours: 14,
    totalHours: 19,
    totalNonBillableHours: 5
  }
]    
这分为两个阶段。首先,它使用groupBy收集相似的值,并使用值将结果提取为数组

然后,它映射到生成的组列表,通过适当地组合字段将每个组减少为单个值

这可能对您没有多大帮助,因为仅仅为了一项任务而引入Ramda这样的库可能是一个荒谬的想法。但你可能会从问题的分解中得到灵感

这里使用的大多数Ramda函数都很容易自己创建,并且在许多方面都非常有用


您可以在屏幕上看到这一点。

这太尴尬了。。。你只是想知道如何将每个资源的所有计费小时数相加吗?这里的问题是什么?@BradEvans,不止这些。我需要在angular应用程序上显示两个单独的对象。在脑海中、在纸上或黑板上浏览代码,或者找人讨论代码,或者使用调试器浏览代码。你应该很快找到你的问题。@torazaburo相信我,我不会发帖的,除非我已经花了几个小时在上面。我迷路了。我同意这个解决方案很尴尬。请参阅下面的任何答案。常见的主题是不要尝试在阵列中执行此操作。这就是为什么很难理解。我很感激你的尝试,但这不是我想要的。我需要属性字段TimeBelabl小时、ToalTimes和Tun-ByBelabl小时更新。@ Tyelrigka我已经更新了答案来考虑Brable属性并输出您正在寻找的结果。这是一个可怕的滥用过滤器,但我不得不承认它导致非常简单的代码。但可悲的是,也没有那么优雅!;-不过,我可能会选择它,而不是这里的其他答案。如果我已经在使用Ramda,我会选择下面的答案。根据人们对可重用部件的期望程度,小时数可以用更通用的prop/get/property函数来编写。maphours可能是“hours”,等等。。。很快你就要建一个图书馆了!很抱歉写信给你。。。我发现您是ramdajs的顶级用户,因此我想问您是否有时间快速查看我的问题,我非常感谢您的反馈。提前谢谢你的专业知识@GibboK:我已经玩过一点了。我会回复的。谢谢斯科特,我真的很感谢你的时间和帮助。
function groupBy(arr, prop) {
  return arr.reduce((result, obj) {
    const key = obj[prop];
    if (!result[key]) result[key] = [];
    result[key].push(obj);
    return result;
  }, {});
}
const process = pipe(
  groupBy(prop('resource')),
  values,
  map(group => reduce((totals, member) => ({
    name: member.name,
    resource: member.resource,
    totalHours: totals.totalHours + member.hours,
    totalBillableHours: totals.totalBillableHours + 
            (member.billable ? member.hours : 0),
    totalNonBillableHours: totals.totalNonBillableHours + 
            (member.billable ? 0 : member.hours)
  }), head(group), group))
);
process(members)
[
  {
    name: "Joe Smith",
    resource: "00530000003mgYGAAY",
    totalBillableHours: 20,
    totalHours: 25,
    totalNonBillableHours: 5
  },
  {
    name: "Jam Smith",
    resource: "00530000003mgYTAAY",
    totalBillableHours: 14,
    totalHours: 19,
    totalNonBillableHours: 5
  }
]