MKN
MKN

Reputation: 497

Merge Array of same level

I have an array which I need to combine with comma-separated of the same level and form a new array.

Input:

let arr = [
  [{ LEVEL: 1, NAME: 'Mark' }, { LEVEL: 1, NAME: 'Adams' }, { LEVEL: 2, NAME: 'Robin' }],
  [{ LEVEL: 3, NAME: 'Williams' }],
  [{ LEVEL: 4, NAME: 'Matthew' }, { LEVEL: 4, NAME: 'Robert' }],
];

Output

[
  [{ LEVEL: 1, NAME: 'Mark,Adams' }, { LEVEL: 2, NAME: 'Robin' }],
  [{ LEVEL: 3, NAME: 'Williams' }],
  [{ LEVEL: 4, NAME: 'Matthew,Robert' }],
];

I tried with the following code but not getting the correct result

let finalArr = [];
arr.forEach(o => {
  let temp = finalArr.find(x => {
    if (x && x.LEVEL === o.LEVEL) {
      x.NAME += ', ' + o.NAME;
      return true;
    }
    if (!temp) finalArr.push(o);
  });
});

console.log(finalArr);

Upvotes: 1

Views: 899

Answers (5)

Hassan Ali Shahzad
Hassan Ali Shahzad

Reputation: 2724

ES6 way:

let say attributes is multidimensional array having multimple entries which need to combine like following:

enter image description here

 let combinedArray = [];
 attributes.map( attributes => {
  combined = combinedArray.concat(...attributes);
 });

Upvotes: 0

KooiInc
KooiInc

Reputation: 122906

Map the array values: every element to an intermediate object, then create the desired object from the resulting entries:

const basicArr = [
  [{"LEVEL":1,"NAME":"Mark"},
   {"LEVEL":1,"NAME":"Adams"},
   {"LEVEL":2,"NAME":"Robin"}   ],
  [{"LEVEL":3,"NAME":"Williams"}],
  [{"LEVEL":4,"NAME":"Matthew"},
   {"LEVEL":4,"NAME":"Robert"}]
];
const leveled = basicArr.map( val => {
    let obj = {};
    val.forEach(v =>  {
      obj[v.LEVEL] = obj[v.LEVEL] || {NAME: []};
      obj[v.LEVEL].NAME = obj[v.LEVEL].NAME.concat(v.NAME);
    });
    return Object.entries(obj)
      .map( ([key, val]) => ({LEVEL: +key, NAME: val.NAME.join(", ")}));
  }
);
console.log(leveled);
.as-console-wrapper { top: 0; max-height: 100% !important; }

if you want to flatten all levels

const basicArr = [
  [{"LEVEL":1,"NAME":"Mark"},
   {"LEVEL":1,"NAME":"Adams"},
   {"LEVEL":2,"NAME":"Robin"}   ],
  [{"LEVEL":3,"NAME":"Williams"}],
  [{"LEVEL":4,"NAME":"Matthew"},
   {"LEVEL":4,"NAME":"Robert"},
   {"LEVEL":2,"NAME":"Cynthia"}],
  [{"LEVEL":3,"NAME":"Jean"},
   {"LEVEL":4,"NAME":"Martha"},
   {"LEVEL":2,"NAME":"Jeff"}],
   
];

const leveled = basicArr.map( val => Object.entries (
      val.reduce( (acc, val) =>  {
        acc[val.LEVEL] = acc[val.LEVEL] || {NAME: []};
        acc[val.LEVEL].NAME = acc[val.LEVEL].NAME.concat(val.NAME);
        return acc;
      }, {}))
      .map( ([key, val]) => ({LEVEL: +key, NAME: val.NAME.join(", ")})) )
  .flat() // (use .reduce((acc, val) => acc.concat(val), []) for IE/Edge)
  .reduce( (acc, val) => {
      const exists = acc.filter(x => x.LEVEL === val.LEVEL);
      if (exists.length) {
        exists[0].NAME = `${val.NAME}, ${exists.map(v => v.NAME).join(", ")}`;
        return acc;
      }
      return [... acc, val];
    }, [] );

console.log(leveled);
.as-console-wrapper { top: 0; max-height: 100% !important; }

Upvotes: 1

Cody
Cody

Reputation: 10015

I'd try to do as much of this in constant time as possible.

var m = new Map();
array.forEach( refine.bind(m) );

function refine({ LABEL, NAME }) {
    var o = this.get(NAME)
      , has = !!o
      , name = NAME
      ;
    if (has) name = `${NAME}, ${o.NAME}`;
    this.delete(NAME);
    this.set(name, { NAME: name, LABEL });
}

var result = Array.from( m.values() );

I haven't tested this as I wrote it on my phone at the airport, but this should at least convey the approach I would advise.

EDIT

Well... looks like the question was edited... So... I'd recommend adding a check at the top of the function to see if it's an array and, if so, call refine with an early return. Something like:

var m = new Map();
array.forEach( refine.bind(m) );

function refine(item) {
    var { LABEL, NAME } = item;
    if (!NAME) return item.forEach( refine.bind(this) );  // assume array
    var o = this.get(NAME)
      , has = !!o
      , name = NAME
      ;
    if (has) name = `${NAME}, ${o.NAME}`;
    this.delete(NAME);
    this.set(name, { NAME: name, LABEL });
}

var result = Array.from( m.values() );

That way, it should work with both your original question and your edit.

EDIT

Looks like the question changed again... I give up.

Upvotes: 1

T.J. Crowder
T.J. Crowder

Reputation: 1074425

Assuming you only want to merge within the same array and not across arrays, and assuming there aren't all that many entries (e.g., fewer than several hundred thousand), the simple thing is to build a new array checking to see if it already has the same level in it:

let result = arr.map(entry => {
    let newEntry = [];
    for (const {LEVEL, NAME} of entry) {
        const existing = newEntry.find(e => e.LEVEL === LEVEL);
        if (existing) {
            existing.NAME += "," + NAME;
        } else {
            newEntry.push({LEVEL, NAME});
        }
    }
    return newEntry;
});

let arr=    [
        [{"LEVEL":1,"NAME":"Mark"},
         {"LEVEL":1,"NAME":"Adams"},
         {"LEVEL":2,"NAME":"Robin"}   ],
        [{"LEVEL":3,"NAME":"Williams"}],
        [{"LEVEL":4,"NAME":"Matthew"},
         {"LEVEL":4,"NAME":"Robert"}]
    ];

let result = arr.map(entry => {
    let newEntry = [];
    for (const {LEVEL, NAME} of entry) {
        const existing = newEntry.find(e => e.LEVEL === LEVEL);
        if (existing) {
            existing.NAME += "," + NAME;
        } else {
            newEntry.push({LEVEL, NAME});
        }
    }
    return newEntry;
});

console.log(result);

If the nested arrays can be truly massively long, you'd want to build a map rather than doing the linear search (.find) each time.

Upvotes: 1

Nina Scholz
Nina Scholz

Reputation: 386624

You could map the outer array and reduce the inner array by finding the same level and add NAME, if found. Otherwise create a new object.

var data = [[{ LEVEL: 1, NAME: "Mark" }, { LEVEL: 1, NAME: "Adams" }, { LEVEL: 2, NAME: "Robin"}], [{ LEVEL: 3, NAME: "Williams" }], [{ LEVEL: 4, NAME: "Matthew" }, { LEVEL: 4, NAME: "Robert" }]],
    result = data.map(a => a.reduce((r, { LEVEL, NAME }) => {
        var temp = r.find(q => q.LEVEL === LEVEL);
        if (temp) temp.NAME += ',' + NAME;
        else r.push({ LEVEL, NAME });
        return r;
    }, []));

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Upvotes: 2

Related Questions