Joshua Swiss
Joshua Swiss

Reputation: 305

Array of Objects: Create new object for each specified key that has a value

I have an array of objects, pulled from a CSV file, like so:

    const data = [
  { name: 'p1', 'date started': 'April 2007', Houston: '375', Dallas: '508', Austin: '', 'El Paso': '1232' },
  { name: 'p2', 'date started': 'April 2017', Houston: '', Dallas: '', Austin: '', 'El Paso': '43' },
  { name: 'p3', 'date started': 'June 2012', Houston: '18789', Dallas: '', Austin: '8977', 'El Paso': '6754656' },
  { name: 'p4', 'date started': 'December 2015', Houston: '878', Dallas: '4556', Austin: '987', 'El Paso': '1456232' }
]

I am creating some charts and tables of the data, and need to create two new keys, 'location' and 'value', and create new objects for each location value in each object, like this:

    const newData = [
  { location: 'Houston', value: '375', name: 'p1', 'date started': 'April 2007' },
  { location: 'Dallas', value: '508', name: 'p1', 'date started': 'April 2007' },
  { location: 'El Paso', value: '1232', name: 'p1', 'date started': 'April 2007' },
  { location: 'El Paso', value: '43', name: 'p2', 'date started': 'April 2017' },
  { location: 'Houston', value: '18789', name: 'p3', 'date started': 'June 2012' },
  { location: 'Austin', value: '8977', name: 'p3', 'date started': 'June 2012' },
  { location: 'El Paso', value: '6754656', name: 'p3', 'date started': 'June 2012' },
  { location: 'Houston', value: '878', name: 'p4', 'date started': 'December 2015' },
  { location: 'Dallas', value: '4556', name: 'p4', 'date started': 'December 2015' },
  { location: 'Austin', value: '987', name: 'p4', 'date started': 'December 2015' },
  { location: 'El Paso', value: '1456232', name: 'p4', 'date started': 'December 2015' }
]

I had to do this for a similar project before, was short on time, and ended up manually editing the original CSV file. I'd rather not do that again. So far I've tried various combinations of map/forEach and Object.keys, with no luck.

Any ideas would be much appreciated!

Upvotes: 3

Views: 140

Answers (4)

Peter Seliger
Peter Seliger

Reputation: 13376

With an entirely generic approach the OP is in control of all key-value-pairs that have to be assigned "as is" for each newly created data item ... and even for the surrogate keys of each data-entry tuple ...

var data = [
  { name: 'p1', 'date started': 'April 2007', Houston: '375', Dallas: '508', Austin: '', 'El Paso': '1232' },
  { name: 'p2', 'date started': 'April 2017', Houston: '', Dallas: '', Austin: '', 'El Paso': '43' },
  { name: 'p3', 'date started': 'June 2012', Houston: '18789', Dallas: '', Austin: '8977', 'El Paso': '6754656' },
  { name: 'p4', 'date started': 'December 2015', Houston: '878', Dallas: '4556', Austin: '987', 'El Paso': '1456232' }
];

var newData = data.reduce(function (collector, dataItem) {
  var
  //itemEntryList = Object.entries(dataItem); // if available, otherwise next line ...
    itemEntryList = Object.keys(dataItem).map(function (key) {
      return [key, dataItem[key]];
    }),
    assignerList = [],
    assignerKey,
    idx = -1,
    keyForDataKey = collector.keyForDataKey,
    keyForDataValue = collector.keyForDataValue,
    protectedKeyList = collector.protectedKeyList;

  // implement a local `reject` ... for all key value pairs that have to be copied "as is".
  while ((assignerKey = itemEntryList[++idx]) && (assignerKey = assignerKey[0])) {
    if (protectedKeyList.some(function (protectedKey) {
      return (assignerKey === protectedKey);
    })) {
      assignerList.push({ key: assignerKey, value: itemEntryList[idx][1] });
      itemEntryList.splice(idx, 1);
      --idx;
    }
  }
  // create new data-item base-structures from the remaining `dataItem` tuples after the `reject` step.
  var dataItemList = itemEntryList.reduce(function (itemList, dataTuple) {

    var tupleValue = dataTuple[1];
    if (tupleValue) {
      var newDataItem = {};
      newDataItem[keyForDataKey] = dataTuple[0];
      newDataItem[keyForDataValue] = tupleValue;

      itemList.push(newDataItem);
    //itemList.push({ location: dataTuple[0], value: tupleValue });
    }
    return itemList;

  }, []);

  // for each new data item ...
  dataItemList.map(function (newDataItem) {
    return assignerList.reduce(function (dataItem, assignerItem) {

      // ... reassign all formerly rejected key value pairs that have to be copied "as is".
      dataItem[assignerItem.key] = assignerItem.value;
      return dataItem;

    }, newDataItem)
  });

  // collect all new data items.
  collector.dataItemList = collector.dataItemList.concat(dataItemList);

  return collector;

}, {

  keyForDataKey:    'location',
  keyForDataValue:  'value',

  protectedKeyList: ['name', 'date started'],
  dataItemList:     []

}).dataItemList;


console.log('data : ', data);
console.log('newData : ', newData);
.as-console-wrapper { max-height: 100%!important; top: 0; }

Upvotes: 1

llama
llama

Reputation: 2537

This becomes pretty clean with destructuring assignment and rest/spread syntax.

const data = [
  { name: 'p1', 'date started': 'April 2007', Houston: '375', Dallas: '508', Austin: '', 'El Paso': '1232' },
  { name: 'p2', 'date started': 'April 2017', Houston: '', Dallas: '', Austin: '', 'El Paso': '43' },
  { name: 'p3', 'date started': 'June 2012', Houston: '18789', Dallas: '', Austin: '8977', 'El Paso': '6754656' },
  { name: 'p4', 'date started': 'December 2015', Houston: '878', Dallas: '4556', Austin: '987', 'El Paso': '1456232' }
];

const result = data.reduce((res, obj) => {
  const date_started = obj["date started"];
  delete obj["date started"];
  const {name, ...rest} = obj;
  
  return [...res, ...Object.entries(rest).map(([k,v]) =>
    ({location:k, value:v, name:name, 'date started':date_started})
  )]
}, []);

console.log(result);


If you wish to avoid the rest parameter in object literals, and you don't want the empty locations, you can create a Set that is used to exclude the non-location keys, and use truthy evaluation to exclude the undesired locations.

var data = [{ name: 'p1', 'date started': 'April 2007', Houston: '375', Dallas: '508', Austin: '', 'El Paso': '1232' }, { name: 'p2', 'date started': 'April 2017', Houston: '', Dallas: '', Austin: '', 'El Paso': '43' }, { name: 'p3', 'date started': 'June 2012', Houston: '18789', Dallas: '', Austin: '8977', 'El Paso': '6754656' }, { name: 'p4', 'date started': 'December 2015', Houston: '878', Dallas: '4556', Austin: '987', 'El Paso': '1456232' }];

var exc = new Set(['name', 'date started']);
var result = data.reduce((r, o) =>
    [
      ...r, 
      ...Object.entries(o)
        .filter(([k, v]) => v && !exc.has(k))
        .map(([k, v]) => ({ location: k, value: v, name: o.name, 'date started': o['date started'] }))
    ]
, []);

console.log(result, null, 2);

Upvotes: 1

Nina Scholz
Nina Scholz

Reputation: 386560

You could iterate all keys of the object and exclude the unwanted propoerties and build new objects for the result set.

var data = [{ name: 'p1', 'date started': 'April 2007', Houston: '375', Dallas: '508', Austin: '', 'El Paso': '1232' }, { name: 'p2', 'date started': 'April 2017', Houston: '', Dallas: '', Austin: '', 'El Paso': '43' }, { name: 'p3', 'date started': 'June 2012', Houston: '18789', Dallas: '', Austin: '8977', 'El Paso': '6754656' }, { name: 'p4', 'date started': 'December 2015', Houston: '878', Dallas: '4556', Austin: '987', 'El Paso': '1456232' }],
    result = data.reduce(function (r, o) {
        Object.keys(o).forEach(function (k)  {
            if (['name', 'date started'].includes(k) || !o[k]) {
                return;
            }
            r.push({ location: k, value: o[k], name: o.name, 'date started': o['date started'] });
        });
        return r;
    }, []);

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Upvotes: 2

bambam
bambam

Reputation: 86

You could iterate through all items which are your old objects and then iterate through all keys in the old object and build a new array based on that, like so

const data = [
        { name: 'p1', 'date started': 'April 2007', Houston: '375', Dallas: '508', Austin: '', 'El Paso': '1232' },
        { name: 'p2', 'date started': 'April 2017', Houston: '', Dallas: '', Austin: '', 'El Paso': '43' },
        { name: 'p3', 'date started': 'June 2012', Houston: '18789', Dallas: '', Austin: '8977', 'El Paso': '6754656' },
        { name: 'p4', 'date started': 'December 2015', Houston: '878', Dallas: '4556', Austin: '987', 'El Paso': '1456232' }
    ];

function transform() {
    
    var newObjects = [];
    data.forEach(item => {
        Object.keys(item).forEach(keyName => {
            if (keyName !== "name" && keyName !== "date started") {
                newObjects.push({
                    location: keyName,
                    value: item[keyName],
                    ["date started"]: item["date started"],
                    name: item["name"]
                })
            }
        });
    });
    return newObjects;
}

console.log(transform(data));

Hope this helps

Upvotes: 1

Related Questions