alim1990
alim1990

Reputation: 4972

Javascript flattening array inside an object

P.S. I tried some stack overflow already answered questions about flattening a nested array inside an object, and they are mentioned in the question below.

I am still working on cleaning an array. I managed to fill empty and null-values arrays using the following:

if (Array.isArray(elem[prop]) || typeof(elem[prop]) === 'object') {
  if (elem[prop].indexOf(null) !== -1 || elem[prop].length === 0) {
    // console.log(elem[prop], prop)
    elem[prop] = '';
  }
}

Assuming I am having the following array:

array = [
  {
    id: 123,
    name: 'Peter',
    phone: '',
    addresses: 
    [
      { 
        address1: 'Manchester, UK', address2: 'London, UK' 
      }, 
      { 
        address1: 'Liverpool, UK', address2: 'NY, USA' 
      }
    ]
  },
  {
    id: 124,
    name: 'Sara',
    phone: '',
    addresses: [{ address1: 'London, UK', address2: 'Paris, FR' }]
  }
];

I need to flatten the address array, so the final array will look like this:

array = [
  {
    id: 123,
    name: 'Peter',
    phone: '',
    addresses_address1: 'Manchester, UK',
    addresses_address2: 'London, UK'
  },
  {
    id: 124,
    name: 'Sara',
    phone: '',
    addresses_address1: 'London, UK',
    addresses_address2: 'Paris, FR'
  }
];

As you say, the first nested array of user id = 123 is added as addresses_addres1 and addresses_address2 and the second nested array has been removed:

{ 
  address1: 'Liverpool, UK', address2: 'NY, USA' 
}

The only reason I am removing other nested arrays because of the naming. Anyway, I tried the following solution from a Stack Overflow answer using concat.apply():

ngOnInit(){
  let props = [];
  var flattened = [];

  // Getting the properties of the array
  props = Array.from(new Set(this.array.flatMap(e => Object.keys(e), [])));
  // console.log(props)
  for (const elem of this.array){
    for (const prop of props) {
      if(Array.isArray(elem[prop])){
        flattened = [].concat.apply([],this.array);
      }
    }
  }
  console.log(flattened)
}

But the consoled array is the same as the original one.

I then tried to use lodash:

// Method 2 using lodash
for (const elem of this.array) {
  for (const prop of props) {
    if (Array.isArray(elem[prop])) {
      elem[prop] = _.flatten(elem[prop])
    }
  }
}
console.log(this.array)

But it didn't works, as it only may work on arrays like this: [[1,2, 'abc'], ['x', True, 'z'], 11].

I tried to do it using Vanilla JavaScript from this article:

//Method 3: Vanilla JavaScript
console.log(this.array.flat())

But no result as well.

I tried the answer from this post on stack overflow about partially flattening an object:

//Method 4:
for (const elem of this.array) {
  for (const prop of props) {
    if (Array.isArray(elem[prop])) {
      const result = elem[prop].map(
      ({prop, ...rest}) => Object.assign(rest, ...Object.keys(prop).map(key => {[elem[prop]+"_"+prop] : elem[prop]})));
    }
  }
}
console.log(this.array)

And no result as well.

Here is a stackblitz describing the issue with all 4 methods.

Upvotes: 0

Views: 188

Answers (2)

pilchard
pilchard

Reputation: 12909

Very similar to the other answer, but using map() and spread syntax to copy each iterated object to avoid mutating the original array, and utilizing destructuring to remove the addresses property before applying its properties to the copied object in nested for...of of loops.

const array = [{ id: 123, name: 'Peter', phone: '', addresses: [{ address1: 'Manchester, UK', address2: 'London, UK' }, { address1: 'Liverpool, UK', address2: 'NY, USA' },], }, { id: 124, name: 'Sara', phone: '', addresses: [{ address1: 'London, UK', address2: 'Paris, FR' }], },];

const flat_array = array.map(({ addresses, ...contact }) => {
  const _contact = { ...contact };

  let i = 1;
  for (const address_obj of addresses) {
    for (const address of Object.values(address_obj)) {
      _contact[`address_${i++}`] = address;
    }
  }

  return _contact;
});

console.log(flat_array);

Or a slightly more obscure solution calling Object.fromEntries() on the flatMap()ed addresses array and copying/merging it with the original contact with Object.assign()

const array = [{ id: 123, name: 'Peter', phone: '', addresses: [{ address1: 'Manchester, UK', address2: 'London, UK' }, { address1: 'Liverpool, UK', address2: 'NY, USA' },], }, { id: 124, name: 'Sara', phone: '', addresses: [{ address1: 'London, UK', address2: 'Paris, FR' }], },];

const flat_array = array.map(({ addresses, ...contact }) => {

  let i = 1;
  const address_object = Object.fromEntries(
    addresses.flatMap((o) => Object.values(o).map((v) => [`address_${i++}`, v]))
  );

  return Object.assign({}, contact, address_object);
});

console.log(flat_array);

Upvotes: 0

Ludolfyn
Ludolfyn

Reputation: 2075

You could try the function below. Just pass the array as an argument. The addresses array can contain any amount of objects, and those objects can contain any amount of entries.

array = [{
    id: 123,
    name: 'Peter',
    phone: '',
    addresses: [{
        address1: 'Manchester, UK',
        address2: 'London, UK'
      },
      {
        address1: 'Liverpool, UK',
        address2: 'NY, USA'
      }
    ]
  },
  {
    id: 124,
    name: 'Sara',
    phone: '',
    addresses: [{
      address1: 'London, UK',
      address2: 'Paris, FR'
    }]
  }
]

function formatArray(array) {
  array.forEach(person => {
    let count = 1
    person.addresses.forEach(addressObj => {
      Object.keys(addressObj).forEach(address => {
        person['address' + count++] = addressObj[address]
      })
    })
    delete person.addresses
  })
  return array
}

console.log(formatArray(array))

Upvotes: 1

Related Questions