2

I have the following JavaScript object:

var example = [{
    country: "US",
    things: {
      weather: 'cloudy'
    }
  },
  {
    country: "US",
    things: {
      resource: 'lead',
      weather: 'sunny'
    }
  },
  {
    country: "MX",
    things: {
      weather: 'sunny'
    }
  },
  {
    country: "MX",
    things: {
      resource: 'gold',
      weather: 'sunny'
    }
  },
  {
    country: "MX",
    things: {
      resource: 'copper'
    }
  },
]

I would like to convert to this format via aggregation.

var out = [{
    country_code: 'US',
    things: {
      resource: ['lead'],
      weather: ['cloudy', 'sunny']
    }
  },
  {
    country_code: 'MX',
    things: {
      resource: ['gold', 'copper'],
      weather: ['sunny'],
    }
  }

]

I have tried to look into using combinations of reduce and map to no avail. It would be great if this example can also serve as a jumping off point for general strategies for data manipulation that may or may not involve the use of the array methods.

1
  • 2
    what you have tried so far? can you post your code here Commented Mar 14, 2019 at 4:54

5 Answers 5

2

Use reduce to iterate over the objects to compile your new object, piecing together the things you want:

const example = [{
    country: "US",
    things: {
      weather: 'cloudy'
    }
  },
  {
    country: "US",
    things: {
      resource: 'lead',
      weather: 'sunny'
    }
  },
  {
    country: "MX",
    things: {
      weather: 'sunny'
    }
  },
  {
    country: "MX",
    things: {
      resource: 'gold',
      weather: 'sunny'
    }
  },
  {
    country: "MX",
    things: {
      resource: 'copper'
    }
  },
]

const out = Object.values(
  example.reduce((a, v) => {
    if (!a[v.country]) {
      a[v.country] = {
        country_code: v.country,
        things: {}
      }
    }

    Object.entries(v.things).forEach(([key, value]) => {
      if (!a[v.country].things[key]) {
        a[v.country].things[key] = []
      }

      if (!a[v.country].things[key].includes(value)) {
        a[v.country].things[key].push(value)
      }
    })

    return a
  }, {})
)

console.log(out)

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

Comments

1

This will show you how to use reduce to get the data that you want using reduce

const example = [{
    country: "US",
    things: {
      weather: "cloudy"
    }
  },
  {
    country: "US",
    things: {
      resource: "lead",
      weather: "sunny"
    }
  },
  {
    country: "MX",
    things: {
      weather: "sunny"
    }
  },
  {
    country: "MX",
    things: {
      resource: "gold",
      weather: "sunny"
    }
  },
  {
    country: "MX",
    things: {
      resource: "copper"
    }
  }
];

const output = example.reduce((acc, current) => {
  const index = acc.findIndex(x => x.country === current.country);
  if (index === -1) {
    const newNode = {
      country: current.country,
      things: {
        resource: current.things.resource ? [current.things.resource] : [],
        weather: current.things.weather ? [current.things.weather] : []
      }
    };
    acc.push(newNode);
  } else {
    current.things.resource && acc[index].things.resource.findIndex(x => x === current.things.resource) === -1 && acc[index].things.resource.push(current.things.resource)


    current.things.weather && acc[index].things.weather.findIndex(x => x === current.things.weather) === -1 && acc[index].things.weather.push(current.things.weather)
  }
  return acc;
}, []);

console.log(output);

Comments

1

You can use reduce function and use findIndex to check if the accumulator have the object with country_code. If it is there then update the array in things object.

var example = [{
    country: "US",
    things: {
      weather: 'cloudy'
    }
  },
  {
    country: "US",
    things: {
      resource: 'lead',
      weather: 'sunny'
    }
  },
  {
    country: "MX",
    things: {
      weather: 'sunny'
    }
  },
  {
    country: "MX",
    things: {
      resource: 'gold',
      weather: 'sunny'
    }
  },
  {
    country: "MX",
    things: {
      resource: 'copper'
    }
  },
]


function finalOut(arr) {
  return arr.reduce(function(acc, curr) {
    let findIndex = acc.findIndex(function(item) {
      return item.country_code === curr.country;
    });
    if (findIndex === -1) {
      acc.push({
        country_code: curr.country,
        things: {
          resource: curr.things.resource ? [curr.things.resource] : [],
          weather: curr.things.weather ? [curr.things.weather] : []
        }
      })
    } else {

      if (curr.things.resource && acc[findIndex].things.resource.indexOf(curr.things.resource) === -1) {
        acc[findIndex].things.resource.push(curr.things.resource);
      }

      if (curr.things.weather && acc[findIndex].things.weather.indexOf(curr.things.weather) === -1) {
        acc[findIndex].things.weather.push(curr.things.weather);
      }

    }

    return acc;
  }, [])
}

console.log(finalOut(example))

Comments

1

Use reduce to iterate over all the items and then arrange them in the right format:

example.reduce((prev,current)=>{

        let index = prev.findIndex(item => item.country_code == current.country);

        if(index>=0){
            if(current.things.resource && !prev[index].things.resource.includes(current.things.resource))
                prev[index].things.resource.push(current.things.resource);
            if(current.things.weather && !prev[index].things.weather.includes(current.things.weather))
                prev[index].things.weather.push(current.things.weather);
        }else{
            prev.push({
                country_code : current.country,
                things : {
                    weather : current.things.weather ? [current.things.weather] : [],
                    resource : current.things.resource ? [current.things.resource] : []
                }
            });
        }
        return prev;
},[]);

Comments

1
  1. Build map of country data using reduce
{
  US: {
    resource: ['lead'],
    weather: ['cloudy', 'sunny'],
  },
  MX: {
    resource: ['gold', 'copper'],
    weather: ['sunny'],
  },
}
  1. Use Object.entries to get an Array of entries from the map
[
  [ 'US', { resource: ['lead'], weather: ['cloudy', 'sunny'] } ],
  [ 'MX', { resource: ['gold', 'copper'], weather: ['sunny'] } ],
]
  1. Map the Array of entries into an Array of Objects with the desired structure

const buildCountriesMap = data => data.reduce((map, { country, things: { weather, resource } }) => {
  if (!map.hasOwnProperty(country)) {
    map[country] = { resource: [], weather: [] };
  }

  const { resource: mapResource, weather: mapWeather } = map[country];

  if (resource && !mapResource.includes(resource)) {
    mapResource.push(resource);
  }
  if (weather && !mapWeather.includes(weather)) {
    mapWeather.push(weather);
  }

  return map;
}, {});

const merge = data => Object.entries(buildCountriesMap(data))
  .map(([country, things]) => ({ country, things }));

const example = [
  {
    country: 'US',
    things: {
      weather: 'cloudy',
    },
  },
  {
    country: 'US',
    things: {
      resource: 'lead',
      weather: 'sunny',
    },
  },
  {
    country: 'MX',
    things: {
      weather: 'sunny',
    },
  },
  {
    country: 'MX',
    things: {
      resource: 'gold',
      weather: 'sunny',
    },
  },
  {
    country: 'MX',
    things: {
      resource: 'copper',
    },
  },
];

console.log(merge(example));

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.