Home > database >  Process certain properties in array of objects
Process certain properties in array of objects

Time:02-11

I am trying to process some properties from an array of objects.

The order is always the same, in that after the second property always comes a range of dates for the next 12 entries, everything after that is to be excluded and sometimes can be two like in this instance or it could be more.

So what I want to do is exclude everything but the keyword and the range of dates.

const data = [{
  "keyword": "foo",
  // This key never changes
  "search_vol": 0,
  // These keys can change when they start, i.e. starts jan-21
  "jul-20": 10,
  "aug-20": 10,
  "sep-20": 10,
  "oct-20": 10,
  "nov-20": 10,
  "dec-20": 10,
  "jan-21": 10,
  "feb-21": 10,
  "mar-21": 10,
  "apr-21": 20,
  "may-21": 10,
  "jun-21": 10,
  // These would change to different properties in name and amount
  "drain": "FALSE",
  "toilet": "TRUE"
}, {
  "keyword": "faa",

  "search_vol": 100,

  "jul-20": 880,
  "aug-20": 880,
  "sep-20": 880,
  "oct-20": 880,
  "nov-20": 880,
  "dec-20": 880,
  "jan-21": 1000,
  "feb-21": 880,
  "mar-21": 1000,
  "apr-21": 720,
  "may-21": 880,
  "jun-21": 880,

  "drain": "TRUE",
  "toilet": "TRUE"
}]

I want to do this so later I can get the max value for the dates on each object and have an entry with the months that hit that max

Expected final output:

const data = [{
  "keyword": "foo",
  "search_vol": 0,
  "jul-20": 10,
  "aug-20": 10,
  "sep-20": 10,
  "oct-20": 10,
  "nov-20": 10,
  "dec-20": 10,
  "jan-21": 10,
  "feb-21": 10,
  "mar-21": 10,
  "apr-21": 20,
  "may-21": 10,
  "jun-21": 10,
  "Max months": ['apr-21'],
  "drain": "FALSE",
  "toilet": "TRUE"
}, {
  "keyword": "faa",
  "search_vol": 100,
  "jul-20": 880,
  "aug-20": 880,
  "sep-20": 880,
  "oct-20": 880,
  "nov-20": 880,
  "dec-20": 880,
  "jan-21": 1000,
  "feb-21": 880,
  "mar-21": 1000,
  "apr-21": 720,
  "may-21": 880,
  "jun-21": 880,
  "Max months": ['jan-21','mar-21'],
  "drain": "TRUE",
  "toilet": "TRUE"
}]

Is it worth separating the processes, first set aside the dates and then get the max, or is it best to do it all in one go?

This is my attempt, excluding the mergin back with the data, but I think is over complicated

const data = [{
  "keyword": "foo",
  "search_vol": 0,

  "jul-20": 10,
  "aug-20": 10,
  "sep-20": 10,
  "oct-20": 10,
  "nov-20": 10,
  "dec-20": 10,
  "jan-21": 10,
  "feb-21": 10,
  "mar-21": 10,
  "apr-21": 20,
  "may-21": 10,
  "jun-21": 10,

  "drain": "FALSE",
  "toilet": "TRUE"
}, {
  "keyword": "faa",
  "search_vol": 100,

  "jul-20": 880,
  "aug-20": 880,
  "sep-20": 880,
  "oct-20": 880,
  "nov-20": 880,
  "dec-20": 880,
  "jan-21": 1000,
  "feb-21": 880,
  "mar-21": 1000,
  "apr-21": 720,
  "may-21": 880,
  "jun-21": 880,

  "drain": "TRUE",
  "toilet": "TRUE"
}]

const headers = Object.keys(data[0]).filter((_, index) => index === 0 || ( index >=  1 && index <= 13))

const dates = data.reduce((acc, obj) => {
  const filtered = headers.reduce((entries, key) => ({ ...entries, [key]: obj[key] }), {})
  acc.push(filtered)
  return acc
}, [])

const months = dates.map(({ keyword, ...dates }) => {
  const max = Math.max(...Object.values(dates))
  
  const maxMonths = Object.entries(dates).reduce((acc, [key, value]) => {
    if (value === max) acc.push(key)
    return acc
  }, [])


  return {
    keyword,
    'Max months': maxMonths,
  }
})

console.log(months)

CodePudding user response:

Here is a solution for you

const data = [
  { 'keyword': 'foo', 'search_vol': 0, 'jul-20': 10, 'aug-20': 10, 'sep-20': 10, 'oct-20': 10, 'nov-20': 10, 'dec-20': 10, 'jan-21': 10, 'feb-21': 10, 'mar-21': 10, 'apr-21': 20, 'may-21': 10, 'jun-21': 10, 'drain': 'FALSE', 'toilet': 'TRUE' },
  { 'keyword': 'faa', 'search_vol': 100, 'jul-20': 880, 'aug-20': 880, 'sep-20': 880, 'oct-20': 880, 'nov-20': 880, 'dec-20': 880, 'jan-21': 1000, 'feb-21': 880, 'mar-21': 1000, 'apr-21': 720, 'may-21': 880, 'jun-21': 880, 'drain': 'TRUE', 'toilet': 'TRUE' }
];

const expected = data.map(_obj => {
  // convert object to an array and filter out the values that are not needed
  const filteredArr = Object.entries(_obj)
    .filter(entry => !['keyword', 'search_vol'].includes(entry[0]))
    .slice(0, 12);

  // find the max value in the array
  const max = Math.max(...filteredArr.map(entry => entry[1]));

  // find the list of months with max value
  const maxMonths = filteredArr.filter(entry => entry[1] === max).map(entry => entry[0]);

  return {
    ..._obj,
    'Max Months': maxMonths
  }
})

console.log(expected);

CodePudding user response:

@abranhe's answer is good, but I'd still be wary of relying on the order of the keys. The keys are sorted when you do Object.keys.

You could replace this:

  const filteredArr = Object.entries(_obj)
    .filter(entry => !['keyword', 'search_vol'].includes(entry[0]))
    .slice(0, 12);

with this:

  const filteredArr = Object.entries(_obj)
    .filter(entry => entry[0].match(/[a-z]{3}-[0-9]{2}/) !== null)

This will be more robust as the code changes over time :)

  • Related