8

I have an array of object that looks like this. I made it look that way using .groupBy with lodash.

States.ts

STATES: Object[] = [
  {
    USA: [
      {
        name: 'Alabama',
        abbreviation: 'AL',
        country: 'USA'
      },
      {
        name: 'Alaska',
        abbreviation: 'AK',
        country: 'USA'
      }
    ]
  },
  {
    Canada: [
      {
        name: 'Alberta',
        abbreviation: 'ALB',
        country: 'Canada'
      }
    ]
  }
];

I need it to look like this:

    stateList:StateDropdownItem[] =[ 
    {
       label: 'USA', 
       items: [
               {label: 'AL', value: 'Alabama'},
               {label: 'AK', value: 'Alaska'},
       ]
    },
    .
    .
   ]

I have tried the code below, but it doesn't work. When I print it to the console appears undefiened even if I try to put an element without the forloop I get the following error Cannot read property 'push' of undefined

TS

dropdownOfStates: StateDropdownItem[];
.
.
dropdownBuilder() {
    const STATES_BY_COUNTRY = this.getStatesByCountry();
    let tempItem;
    for (let i = 0; i < STATES_BY_COUNTRY.length; i++) {
      tempItem = STATES_BY_COUNTRY[i];
      this.dropdownOfStates.push(
        new StateDropdownItem('KEY COUNTRY VALUE HERE', [
          tempItem.abbreviation,
          tempItem.name
        ])
      );
    }
  }

Console.log after printing the result of using .groupBy with lodash groupby objects by country

7
  • 1
    Hi, welcome to StackOverflow. As it stands, the STATES array is not valid. Is it supposed to be an array of objects? Or a single object with values USA, Canada etc Commented Aug 25, 2018 at 6:48
  • It's an array of a module of type State that i created in a file called state.module.ts export class State { abbreviation: string; name: string; country: string; constructor(abbreviation: string, name: string, country: string) { this.abbreviation = abbreviation; this.name = name; this.country = country; } } Commented Aug 25, 2018 at 6:50
  • 1
    Okay, can you please edit your question so that the code would compile? Thanks Commented Aug 25, 2018 at 6:51
  • @user184994 done Commented Aug 25, 2018 at 6:52
  • 1
    No, that still wouldn't compile. You can try calling JSON.stringify on the object coming back from lodash, that would help. Commented Aug 25, 2018 at 6:53

2 Answers 2

13

You can use the map function of arrays to transform their structure.

The code below should transform it into the structure you want. You can click "Run code snippet" to see the output

let states = [
  {
    USA: [
      {
        name: 'Alabama',
        abbreviation: 'AL',
        country: 'USA'
      },
      {
        name: 'Alaska',
        abbreviation: 'AK',
        country: 'USA'
      }
    ]
  },
  {
    Canada: [
      {
        name: 'Alberta',
        abbreviation: 'ALB',
        country: 'Canada'
      }
    ]
  }
];

// Use the code below to transform 

let res = states.map((val) => {
  let country = Object.keys(val)[0]; // Get the name of the country, e.g. USA
  return { // Return the new object structure
    label: country,
    items: val[country].map((item) => ({label: item.abbreviation, value: item.name}))
  }
});

// Log the value
console.log(res);

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

1 Comment

Thanks so much! you are a genius. I was thinking to use .map but I wasn't sure on how to do it. Thanks!!
2

Here is somewhat more concise method of doing this with ES6 and reduce, Object.keys and map:

var data = [ { USA: [ { name: 'Alabama', abbreviation: 'AL', country: 'USA' }, { name: 'Alaska', abbreviation: 'AK', country: 'USA' } ] }, { Canada: [ { name: 'Alberta', abbreviation: 'ALB', country: 'Canada' } ] } ];

const result = data.reduce((r,c) => Object.keys(c).map(x => r.push({ label: x, items: c[x].map(y => ({label: y.abbreviation, value: y.name }))})) && r, [])

console.log(result)

This is the expanded variant:

var data = [ { USA: [ { name: 'Alabama', abbreviation: 'AL', country: 'USA' }, { name: 'Alaska', abbreviation: 'AK', country: 'USA' } ] }, { Canada: [ { name: 'Alberta', abbreviation: 'ALB', country: 'Canada' } ] } ];

const result = data.reduce((r,c) => {
   Object.keys(c).map(x => {
     r.push({ label: x, items: c[x].map(y => ({label: y.abbreviation, value: y.name }))})})
	return r}, [])

console.log(result)

3 Comments

Akrion, your solution works fine it compiles and runs. Unfortunately TSlint doesn't like the r.push TSlint thorws error : [ts] Property 'push' does not exist on type 'Object'. Do you have an alternative for this warning?
Interesting. Try the "longer" version I just added.
There is Automapper, it would be easy for you to use this if you have .Net experience too... npmjs.com/package/automapper-ts

Your Answer

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