Group Array of Objects of same level

久未见 提交于 2020-01-04 02:17:04

问题


I have an array which has different level of approvers. I need to combine the object of similar level and group them with the Name+counter.

"APPROVERS": [
        {
            "LEVEL": "L5",
            "EMAIL": "FTEST@TEST.COM",
            "FULLNAME": "FNAME",
            "POSITION": "FPOS",
            "SLA": "48",
            "STATUS": "INITIAL"
        },
        {
            "LEVEL": "L4",
            "EMAIL": "JTEST@TEST.COM",
            "FULLNAME": "JNAME",
            "POSITION": "JPOS",
            "SLA": "48",
            "STATUS": "INITIAL"
        },
        {
            "LEVEL": "L5",
            "EMAIL": "LTEST@TEST.COM",
            "FULLNAME": "LNAME",
            "POSITION": "GPOS",
            "SLA": "48",
            "STATUS": "INITIAL"
        },
        {
            "LEVEL": "L5",
            "EMAIL": "TTEST@TEST.COM",
            "FULLNAME": "TNAME",
            "POSITION": "CPOS",
            "SLA": "48",
            "STATUS": "INITIAL"
        }  
    ]

I need to combine the objects of same level into one and provide the unique name.

 "APPROVERS": [
        {
            "LEVEL": "L5",
            "EMAIL1": "FTEST@TEST.COM",
            "FULLNAME1": "FNAME",
            "POSITION1": "FPOS",
            "SLA1": "48",
            "STATUS1": "INITIAL",
            "EMAIL2": "LTEST@TEST.COM",
            "FULLNAME2": "LNAME",
            "POSITION2": "GPOS",
            "SLA2": "48",
            "STATUS2": "INITIAL",
            "EMAIL3": "TTEST@TEST.COM",
            "FULLNAME3": "TNAME",
            "POSITION3": "CPOS",
            "SLA3": "48",
            "STATUS3": "INITIAL"
        },
        {
            "LEVEL": "L4",
            "EMAIL": "JTEST@TEST.COM",
            "FULLNAME": "JNAME",
            "POSITION": "JPOS",
            "SLA": "48",
            "STATUS": "INITIAL"
        } 
    ]

I tried only to combine EMAIL by looping the array but not able to achieve the result.Kindly suggest.

var result = [];
    var i=0
    APPROVERS.forEach(function(obj) {
      var id = obj.LEVEL
      if(!this[id]) result.push(this[id] = obj);
      else this[id].EMAIL += obj.EMAIL+i;
      i++;
    }, Object.create(null));

    console.log(result)

回答1:


By putting the tag lodash I guess you don't mind using it. I'm not sure you understand it, but I've tried my best to balance between succinctness and readability.

function groupByLevel(approvers) {

  const group = _.groupBy(approvers, 'LEVEL');
  // console.log(group); // try logging to see what we have

  return Object.entries(group).map( ([LEVEL, array]) => {

    return array.reduce((acc, cur, idx) => ({
      ...acc,
      ['EMAIL'    + (idx + 1)]: cur.EMAIL    ,
      ['FULLNAME' + (idx + 1)]: cur.FULLNAME ,
      ['POSITION' + (idx + 1)]: cur.POSITION ,
      ['SLA'      + (idx + 1)]: cur.SLA      ,
      ['STATUS'   + (idx + 1)]: cur.STATUS   ,

    }), { LEVEL });
  })
}












var APPROVERS = [
  {
    LEVEL: 'L5',
    EMAIL: 'FTEST@TEST.COM',
    FULLNAME: 'FNAME',
    POSITION: 'FPOS',
    SLA: '48',
    STATUS: 'INITIAL'
  },
  {
    LEVEL: 'L4',
    EMAIL: 'JTEST@TEST.COM',
    FULLNAME: 'JNAME',
    POSITION: 'JPOS',
    SLA: '48',
    STATUS: 'INITIAL'
  },
  {
    LEVEL: 'L5',
    EMAIL: 'LTEST@TEST.COM',
    FULLNAME: 'LNAME',
    POSITION: 'GPOS',
    SLA: '48',
    STATUS: 'INITIAL'
  },
  {
    LEVEL: 'L5',
    EMAIL: 'TTEST@TEST.COM',
    FULLNAME: 'TNAME',
    POSITION: 'CPOS',
    SLA: '48',
    STATUS: 'INITIAL'
  }
]
console.log(groupByLevel(APPROVERS))
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.js"></script>



回答2:


You can do this by grouping the elements of the same level in an object associating levels with an array of elements, like so:

{
  "L5": [
    {
      "EMAIL": "FTEST@TEST.COM",
      "FULLNAME": "FNAME",
      "POSITION": "FPOS",
      "SLA": "48",
      "STATUS": "INITIAL"
    },
    ...
  ],
  "L4": [
    {
      "EMAIL": "JTEST@TEST.COM",
      "FULLNAME": "JNAME",
      "POSITION": "JPOS",
      "SLA": "48",
      "STATUS": "INITIAL"
    }
  ]
}

Then iterate over the levels and create an array of unique levels which hold each elements with their unique set of keys EMAIL{i}, FULLNAME{i}, POSITION{i}, SLA{i} and STATUS{i}:

[
  {
    "LEVEL": "L5",
    "EMAIL1": "FTEST@TEST.COM",
    "FULLNAME1": "FNAME",
    "POSITION1": "FPOS",
    "SLA1": "48",
    "STATUS1": "INITIAL",
    ...
  },
  {
    "LEVEL": "L4",
    "EMAIL1": "JTEST@TEST.COM",
    "FULLNAME1": "JNAME",
    "POSITION1": "JPOS",
    "SLA1": "48",
    "STATUS1": "INITIAL"
  }
]

Here is the full code:

// prepare an intermediate representation of your data { level => approver[] }
const grouped = data['APPROVERS'].reduce((approvers, approver) => {
  const { LEVEL, ...props } = approver;
  approvers[LEVEL] = approvers[LEVEL] ? approvers[LEVEL].concat([props]) : [props]
  return approvers;
}, {})

// helper function to append a suffix to all keys of a given object
const suffixKeys = (obj, suffix) => {
  return Object.entries(obj).reduce((result, [key, value]) => {
    return { ...result, [key+suffix]: value } 
  }, {});
}

// combine the objects into an array using the intermediate representation
const result = Object.entries(grouped).map(([name, group]) => {
  return group.reduce((grouped, current, i) => {
    return { ...grouped, ...suffixKeys(current, i+1) }
  }, { LEVEL: name });
});

console.log(result)
<script>const data={APPROVERS:[{LEVEL:"L5",EMAIL:"FTEST@TEST.COM",FULLNAME:"FNAME",POSITION:"FPOS",SLA:"48",STATUS:"INITIAL"},{LEVEL:"L4",EMAIL:"JTEST@TEST.COM",FULLNAME:"JNAME",POSITION:"JPOS",SLA:"48",STATUS:"INITIAL"},{LEVEL:"L5",EMAIL:"LTEST@TEST.COM",FULLNAME:"LNAME",POSITION:"GPOS",SLA:"48",STATUS:"INITIAL"},{LEVEL:"L5",EMAIL:"TTEST@TEST.COM",FULLNAME:"TNAME",POSITION:"CPOS",SLA:"48",STATUS:"INITIAL"}]};</script>

However, in my opinion, the intermediate format would be a lot easier to work with.




回答3:


The following algorithm achieves exactly what you want:

let APPROVERS = [
  {
      "LEVEL": "L5",
      "EMAIL": "FTEST@TEST.COM",
      "FULLNAME": "FNAME",
      "POSITION": "FPOS",
      "SLA": "48",
      "STATUS": "INITIAL"
  },
  {
      "LEVEL": "L4",
      "EMAIL": "JTEST@TEST.COM",
      "FULLNAME": "JNAME",
      "POSITION": "JPOS",
      "SLA": "48",
      "STATUS": "INITIAL"
  },
  {
      "LEVEL": "L5",
      "EMAIL": "LTEST@TEST.COM",
      "FULLNAME": "LNAME",
      "POSITION": "GPOS",
      "SLA": "48",
      "STATUS": "INITIAL"
  },
  {
      "LEVEL": "L5",
      "EMAIL": "TTEST@TEST.COM",
      "FULLNAME": "TNAME",
      "POSITION": "CPOS",
      "SLA": "48",
      "STATUS": "INITIAL"
  }  
];

var result = {};
var compareLevel = {};

for (let index = 0; index < APPROVERS.length; index++) {
  if(Object.keys(compareLevel).includes(APPROVERS[index].LEVEL)){

    for (const key in APPROVERS[index]) {
      if(key == 'LEVEL') continue;
      let keyIndex = compareLevel[APPROVERS[index].LEVEL] + 1;
      result[APPROVERS[index].LEVEL][key + keyIndex] = APPROVERS[index][key];
    }
    
    compareLevel[APPROVERS[index].LEVEL]++;
  }else{
    result[APPROVERS[index].LEVEL] = APPROVERS[index];
    compareLevel[APPROVERS[index].LEVEL] = 1;
  }
}

let resultArr = Object.values(result);
console.log(Object.values(result));



回答4:


I see that there are a lot of good answer here (that i will upvote), but as i promised, i deliver to you how i would taclke your problem. Let me show the code

var theApprovers =  [
    {
        "LEVEL": "L5",
        "EMAIL": "FTEST@TEST.COM",
        "FULLNAME": "FNAME",
        "POSITION": "FPOS",
        "SLA": "48",
        "STATUS": "INITIAL"
    },
    {
        "LEVEL": "L4",
        "EMAIL": "JTEST@TEST.COM",
        "FULLNAME": "JNAME",
        "POSITION": "JPOS",
        "SLA": "48",
        "STATUS": "INITIAL"
    },
    {
        "LEVEL": "L5",
        "EMAIL": "LTEST@TEST.COM",
        "FULLNAME": "LNAME",
        "POSITION": "GPOS",
        "SLA": "48",
        "STATUS": "INITIAL"
    },
    {
        "LEVEL": "L5",
        "EMAIL": "TTEST@TEST.COM",
        "FULLNAME": "TNAME",
        "POSITION": "CPOS",
        "SLA": "48",
        "STATUS": "INITIAL"
    }  
]

function groupBy (collection, key) { // based on https://stackoverflow.com/a/34890276/903998
  return collection.reduce(function(grouped, element) {
    (grouped[element[key]] = grouped[element[key]] || []).push(element);
    return grouped;
  }, {});
};

function arrangeApprovers(approvers) {
  // first group by level
  var groupedByLevel = groupBy(approvers,"LEVEL")
  // then for each level do the arrange as you need
  var arrayOflevelWithApprovers = Object.keys(groupedByLevel).map(function(level) {
    var approversOfSameLevel = groupedByLevel[level]
    var levelWithApprovers = {"LEVEL" : level}
    approversOfSameLevel.forEach(function(approber, index) {
      var suffixNumber = index+1
      levelWithApprovers["EMAIL"+suffixNumber] = approber["EMAIL"]
      levelWithApprovers["FULLNAME"+suffixNumber] = approber["FULLNAME"]
      levelWithApprovers["POSITION"+suffixNumber] = approber["POSITION"]
      levelWithApprovers["SLA"+suffixNumber] = approber["SLA"]
      levelWithApprovers["STATUS"+suffixNumber] = approber["STATUS"]
    })
    return levelWithApprovers
  });
  return arrayOflevelWithApprovers;
}

var theApproversArrenged = arrangeApprovers(theApprovers) // so they are arrangen in the convenient fashion..
console.log(theApproversArrenged)

https://repl.it/repls/TruthfulLivelyInterface

You could see that inside theAprroversArrenged the elements are arranged in the fashion you need.

Disclaimer do note that for level4 (and for those levels with an unique approver) the generated records will refer to his attributes with the suffix of 1. Hope that to not be something really annoying for your purposes.




回答5:


After you group the array by LEVEL, you can map the groups. Check the group's length. If it has a single item, you can return that item. If it has more than 1 one, you'll need to map the items, and map the keys of the items to include the item's index + 1, and then merge all items to a single object:

const fn = (arr, groupKey) => {
  const groups = _.groupBy(arr, groupKey);
  
  return _.map(groups, group =>
    group.length > 1 ? // if there's more than one item
      _.merge(... // merge all items
        _.map(group, (item, idx) => _.mapKeys( // map the items in the group
          item, (v, k) => k === groupKey ? k : `${k}${idx + 1}` // add the index to all keys, but LEVEL
        ))
      )
    :
    group[0]
  )
};

const data = {APPROVERS:[{LEVEL:"L5",EMAIL:"FTEST@TEST.COM",FULLNAME:"FNAME",POSITION:"FPOS",SLA:"48",STATUS:"INITIAL"},{LEVEL:"L4",EMAIL:"JTEST@TEST.COM",FULLNAME:"JNAME",POSITION:"JPOS",SLA:"48",STATUS:"INITIAL"},{LEVEL:"L5",EMAIL:"LTEST@TEST.COM",FULLNAME:"LNAME",POSITION:"GPOS",SLA:"48",STATUS:"INITIAL"},{LEVEL:"L5",EMAIL:"TTEST@TEST.COM",FULLNAME:"TNAME",POSITION:"CPOS",SLA:"48",STATUS:"INITIAL"}]};

const result = fn(data.APPROVERS, 'LEVEL')

console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.js"></script>


来源:https://stackoverflow.com/questions/59435861/group-array-of-objects-of-same-level

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!