最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Group Array of Objects of same level - Stack Overflow

programmeradmin0浏览0评论

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

"APPROVERS": [
        {
            "LEVEL": "L5",
            "EMAIL": "[email protected]",
            "FULLNAME": "FNAME",
            "POSITION": "FPOS",
            "SLA": "48",
            "STATUS": "INITIAL"
        },
        {
            "LEVEL": "L4",
            "EMAIL": "[email protected]",
            "FULLNAME": "JNAME",
            "POSITION": "JPOS",
            "SLA": "48",
            "STATUS": "INITIAL"
        },
        {
            "LEVEL": "L5",
            "EMAIL": "[email protected]",
            "FULLNAME": "LNAME",
            "POSITION": "GPOS",
            "SLA": "48",
            "STATUS": "INITIAL"
        },
        {
            "LEVEL": "L5",
            "EMAIL": "[email protected]",
            "FULLNAME": "TNAME",
            "POSITION": "CPOS",
            "SLA": "48",
            "STATUS": "INITIAL"
        }  
    ]

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

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

I tried only to bine 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)

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

"APPROVERS": [
        {
            "LEVEL": "L5",
            "EMAIL": "[email protected]",
            "FULLNAME": "FNAME",
            "POSITION": "FPOS",
            "SLA": "48",
            "STATUS": "INITIAL"
        },
        {
            "LEVEL": "L4",
            "EMAIL": "[email protected]",
            "FULLNAME": "JNAME",
            "POSITION": "JPOS",
            "SLA": "48",
            "STATUS": "INITIAL"
        },
        {
            "LEVEL": "L5",
            "EMAIL": "[email protected]",
            "FULLNAME": "LNAME",
            "POSITION": "GPOS",
            "SLA": "48",
            "STATUS": "INITIAL"
        },
        {
            "LEVEL": "L5",
            "EMAIL": "[email protected]",
            "FULLNAME": "TNAME",
            "POSITION": "CPOS",
            "SLA": "48",
            "STATUS": "INITIAL"
        }  
    ]

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

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

I tried only to bine 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)
Share Improve this question asked Dec 21, 2019 at 11:39 ManiMani 7513 gold badges10 silver badges28 bronze badges 3
  • one approach to group by multiple field is to first generate a field containing both fields (like concatenating) in a single additional field (a name for that Field could be "nameAndCounter"), then you use a group by like this -> stackoverflow./a/34890276/903998 over that field to get the desired aggregation.. if you need some code i can show you – Victor Commented Dec 21, 2019 at 11:45
  • Yes ,that would help. Thank you – Mani Commented Dec 21, 2019 at 12:10
  • 2 sorry for the delay, i'm elaborating an example.. my parents just came to quick visit to his son.. (it happens from time to time.. xD) – Victor Commented Dec 21, 2019 at 13:35
Add a ment  | 

5 Answers 5

Reset to default 3

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: '[email protected]',
    FULLNAME: 'FNAME',
    POSITION: 'FPOS',
    SLA: '48',
    STATUS: 'INITIAL'
  },
  {
    LEVEL: 'L4',
    EMAIL: '[email protected]',
    FULLNAME: 'JNAME',
    POSITION: 'JPOS',
    SLA: '48',
    STATUS: 'INITIAL'
  },
  {
    LEVEL: 'L5',
    EMAIL: '[email protected]',
    FULLNAME: 'LNAME',
    POSITION: 'GPOS',
    SLA: '48',
    STATUS: 'INITIAL'
  },
  {
    LEVEL: 'L5',
    EMAIL: '[email protected]',
    FULLNAME: 'TNAME',
    POSITION: 'CPOS',
    SLA: '48',
    STATUS: 'INITIAL'
  }
]
console.log(groupByLevel(APPROVERS))
<script src="https://cdnjs.cloudflare./ajax/libs/lodash.js/4.17.15/lodash.js"></script>

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": "[email protected]",
      "FULLNAME": "FNAME",
      "POSITION": "FPOS",
      "SLA": "48",
      "STATUS": "INITIAL"
    },
    ...
  ],
  "L4": [
    {
      "EMAIL": "[email protected]",
      "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": "[email protected]",
    "FULLNAME1": "FNAME",
    "POSITION1": "FPOS",
    "SLA1": "48",
    "STATUS1": "INITIAL",
    ...
  },
  {
    "LEVEL": "L4",
    "EMAIL1": "[email protected]",
    "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 } 
  }, {});
}

// bine 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:"[email protected]",FULLNAME:"FNAME",POSITION:"FPOS",SLA:"48",STATUS:"INITIAL"},{LEVEL:"L4",EMAIL:"[email protected]",FULLNAME:"JNAME",POSITION:"JPOS",SLA:"48",STATUS:"INITIAL"},{LEVEL:"L5",EMAIL:"[email protected]",FULLNAME:"LNAME",POSITION:"GPOS",SLA:"48",STATUS:"INITIAL"},{LEVEL:"L5",EMAIL:"[email protected]",FULLNAME:"TNAME",POSITION:"CPOS",SLA:"48",STATUS:"INITIAL"}]};</script>

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

The following algorithm achieves exactly what you want:

let APPROVERS = [
  {
      "LEVEL": "L5",
      "EMAIL": "[email protected]",
      "FULLNAME": "FNAME",
      "POSITION": "FPOS",
      "SLA": "48",
      "STATUS": "INITIAL"
  },
  {
      "LEVEL": "L4",
      "EMAIL": "[email protected]",
      "FULLNAME": "JNAME",
      "POSITION": "JPOS",
      "SLA": "48",
      "STATUS": "INITIAL"
  },
  {
      "LEVEL": "L5",
      "EMAIL": "[email protected]",
      "FULLNAME": "LNAME",
      "POSITION": "GPOS",
      "SLA": "48",
      "STATUS": "INITIAL"
  },
  {
      "LEVEL": "L5",
      "EMAIL": "[email protected]",
      "FULLNAME": "TNAME",
      "POSITION": "CPOS",
      "SLA": "48",
      "STATUS": "INITIAL"
  }  
];

var result = {};
var pareLevel = {};

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

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

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

Here is how I would tackle your problem:

var theApprovers =  [
    {
        "LEVEL": "L5",
        "EMAIL": "[email protected]",
        "FULLNAME": "FNAME",
        "POSITION": "FPOS",
        "SLA": "48",
        "STATUS": "INITIAL"
    },
    {
        "LEVEL": "L4",
        "EMAIL": "[email protected]",
        "FULLNAME": "JNAME",
        "POSITION": "JPOS",
        "SLA": "48",
        "STATUS": "INITIAL"
    },
    {
        "LEVEL": "L5",
        "EMAIL": "[email protected]",
        "FULLNAME": "LNAME",
        "POSITION": "GPOS",
        "SLA": "48",
        "STATUS": "INITIAL"
    },
    {
        "LEVEL": "L5",
        "EMAIL": "[email protected]",
        "FULLNAME": "TNAME",
        "POSITION": "CPOS",
        "SLA": "48",
        "STATUS": "INITIAL"
    }  
]
    
function groupBy (collection, key) { // based on https://stackoverflow./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.

Note: 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 is not something really annoying for your purposes.

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:"[email protected]",FULLNAME:"FNAME",POSITION:"FPOS",SLA:"48",STATUS:"INITIAL"},{LEVEL:"L4",EMAIL:"[email protected]",FULLNAME:"JNAME",POSITION:"JPOS",SLA:"48",STATUS:"INITIAL"},{LEVEL:"L5",EMAIL:"[email protected]",FULLNAME:"LNAME",POSITION:"GPOS",SLA:"48",STATUS:"INITIAL"},{LEVEL:"L5",EMAIL:"[email protected]",FULLNAME:"TNAME",POSITION:"CPOS",SLA:"48",STATUS:"INITIAL"}]};

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

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

发布评论

评论列表(0)

  1. 暂无评论