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

时间:2016-12-01 02:17:31

标签: javascript

我需要将对象合并在一起。 resource属性决定了对象是否可以合并。要确定hours属性值的位置,请使用billable属性。我希望数组看起来像这样

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}]

最终结果的billablehours属性状态无关紧要。

这是代码。似乎无法弄清楚它为什么不起作用。

http://codepen.io/anon/pen/MbOdmV?editors=0002

members = [
{billable: true, hours: 15, name: "Joe Smith", resource: "00530000003mgYGAAY", totalBillableHours: 0, totalHours: 0, totalNonBillableHours: 0},
{billable: true, hours: 5, name: "Joe Smith", resource: "00530000003mgYGAAY", totalBillableHours: 0, totalHours: 0, totalNonBillableHours: 0},
{billable: false, hours: 5, name: "Joe Smith", resource: "00530000003mgYGAAY", totalBillableHours: 0, totalHours: 0, totalNonBillableHours: 0},
{billable: false, hours: 5, name: "Jan Smith", resource: "00530000003mgYTAAY", totalBillableHours: 0, totalHours: 0, totalNonBillableHours: 0},
{billable: true, hours: 12, name: "Jan Smith", resource: "00530000003mgYTAAY", totalBillableHours: 0, totalHours: 0, totalNonBillableHours: 0},
{billable: true, hours: 2, name: "Jan Smith", resource: "00530000003mgYTAAY", totalBillableHours: 0, totalHours: 0, totalNonBillableHours: 0}  
];

for (i = 0; i < members.length; i++) {
  var member = members[i];

  if (member.resource == members[i + 1].resource) {

    if(member.billable == true) {
      member.totalBillableHours += members[i + 1].hours;                     
    }
    else {
      member.totalNonBillableHours += members[i + 1].hours;
    }
    member.totalHours += members[i + 1].hours;
    members.splice(i + 1, 1);
    --i;
    if (members[i + 2] == undefined) {
      break;
    }           

  }
}
console.log(members);

4 个答案:

答案 0 :(得分:2)

在迭代数组时从数组中删除项目时会非常棘手。

我在这里以更实用的方式重写了您的解决方案:http://codepen.io/tinacious/pen/gLXJow?editors=1011

ArgumentError: unknown option: {:profile=>#<Selenium::WebDriver::Firefox::Profile:0x000000068ca798 @model=nil, @native_events=true, @secure_ssl=false, @untrusted_issuer=true, @load_no_focus_lib=false, @additional_prefs={}, @extensions={}>}

结果输出是您正在寻找的:

var members = [
  {billable: true, hours: 15, name: "Joe Smith", resource: "00530000003mgYGAAY", totalBillableHours: 0, totalHours: 0, totalNonBillableHours: 0},
  {billable: true, hours: 5, name: "Joe Smith", resource: "00530000003mgYGAAY", totalBillableHours: 0, totalHours: 0, totalNonBillableHours: 0},
  {billable: false, hours: 5, name: "Joe Smith", resource: "00530000003mgYGAAY", totalBillableHours: 0, totalHours: 0, totalNonBillableHours: 0},
  {billable: false, hours: 5, name: "Jan Smith", resource: "00530000003mgYTAAY", totalBillableHours: 0, totalHours: 0, totalNonBillableHours: 0},
  {billable: true, hours: 12, name: "Jan Smith", resource: "00530000003mgYTAAY", totalBillableHours: 0, totalHours: 0, totalNonBillableHours: 0},
  {billable: true, hours: 2, name: "Jam Smith", resource: "00530000003mgYTAAY", totalBillableHours: 0, totalHours: 0, totalNonBillableHours: 0}  
];

function combineMembers(members) {
  var combinedMembers = {};

  members.forEach(function (member) {
    var resourceId = member.resource;
    var typeOfHour = member.billable ? 'totalBillableHours' : 'totalNonBillableHours';

    if (!combinedMembers[resourceId]) {
      combinedMembers[resourceId] = Object.assign({}, member);
    }

    combinedMembers[resourceId][typeOfHour] += member.hours;      
    combinedMembers[resourceId].totalHours += member.hours;
  });

  return Object.keys(combinedMembers).map(function (resourceId) {
    return combinedMembers[resourceId];
  });
}

console.log(combineMembers(members));

答案 1 :(得分:2)

问题是你依赖于每条记录的相邻记录。相反,您可以通过member保持每个resource的计数。由于resource是唯一的,因此您可以将其用作保持计数的对象的属性 - 将其视为关键字。然后,您可以将每条记录中的小时数添加到相应的对象中。

这是我的尝试:http://codepen.io/anon/pen/bBYyPa

var members = [
{billable: true, hours: 15, name: "Joe Smith", resource: "00530000003mgYGAAY", totalBillableHours: 0, totalHours: 0, totalNonBillableHours: 0},
{billable: true, hours: 5, name: "Joe Smith", resource: "00530000003mgYGAAY", totalBillableHours: 0, totalHours: 0, totalNonBillableHours: 0},
{billable: false, hours: 5, name: "Joe Smith", resource: "00530000003mgYGAAY", totalBillableHours: 0, totalHours: 0, totalNonBillableHours: 0},
{billable: false, hours: 5, name: "Jan Smith", resource: "00530000003mgYTAAY", totalBillableHours: 0, totalHours: 0, totalNonBillableHours: 0},
{billable: true, hours: 12, name: "Jan Smith", resource: "00530000003mgYTAAY", totalBillableHours: 0, totalHours: 0, totalNonBillableHours: 0},
{billable: true, hours: 2, name: "Jam Smith", resource: "00530000003mgYTAAY", totalBillableHours: 0, totalHours: 0, totalNonBillableHours: 0}  
];

var membersObj = {};

for (i = 0; i < members.length; i++) {

  var member = members[i];
  if (!membersObj[member.resource]){
    membersObj[member.resource] = members[i];
  }

  if(member.billable){
    membersObj[member.resource].totalBillableHours += member.hours;
  } else {
    membersObj[member.resource].totalNonBillableHours += member.hours;
  }

  membersObj[member.resource].totalHours += member.hours;

}
console.log(membersObj);

当然,这会让你返回一个对象而不是一个数组,但如果需要可以转换它。

这是输出:

{ 
'00530000003mgYGAAY': 
   { billable: true,
     hours: 15,
     name: 'Joe Smith',
     resource: '00530000003mgYGAAY',
     totalBillableHours: 20,
     totalHours: 25,
     totalNonBillableHours: 5 },
'00530000003mgYTAAY': 
   { billable: false,
     hours: 5,
     name: 'Jan Smith',
     resource: '00530000003mgYTAAY',
     totalBillableHours: 14,
     totalHours: 19,
     totalNonBillableHours: 5 } 
}

答案 2 :(得分:1)

一种技术是循环遍历每个成员,找到第一个拥有该资源的成员,更新其中的总数,然后进行过滤以便仅保留第一次出现。

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]
}

首先取totals,即

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};
  });
}

sum可以写成

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;
  }, {});
}

答案 3 :(得分:1)

这是使用Ramda库的版本(免责声明:我是其中一位作者):

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)并将结果提取为数组(使用values)。

然后,它会映射生成的组列表,通过合并这些字段将每个组减少为单个值。

这对你来说可能没什么帮助,因为拉一个像Ramda这样的图书馆来完成一项任务可能是一个荒谬的想法。但是你可能会在问题崩溃时获得灵感。

这里使用的大多数Ramda函数都很容易自己创建,对于很多用途非常有用。

您可以在 Ramda REPL 上看到这一点。

相关问题