1

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);
5
  • is the array always ordered sequentially by LEVEL? Commented Dec 31, 2019 at 15:09
  • no.. it can come different order as well Commented Dec 31, 2019 at 15:09
  • 1. Keep in mind that arr doesn't contain the objects; it contains arrays which contain the objects 2. you're calling find() on finalArr, surely that's not what you intended to do? Commented Dec 31, 2019 at 15:10
  • 1
    The resulting nested array is still quite an odd structure. Are you saying you should only merge within a nested array? Or across nested arrays in the same outer array? Are they always two levels deep, never one level or three or more...? Commented Dec 31, 2019 at 15:10
  • Only the nested arrays Commented Dec 31, 2019 at 15:10

5 Answers 5

2

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

Sign up to request clarification or add additional context in comments.

Comments

1

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.

Comments

1

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.

Comments

1

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

Comments

0

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.