3

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!

4 Answers 4

2

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; }

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

2 Comments

That is super clever, works well, and will save me a ton of time! Thanks a million!
@JoshuaSwiss: In real life, don't use logical operators in place of flow control. You'll get serious push-back from people who need to read your code. A more sensible solution will look like this: jsfiddle.net/5fvLc8mm It uses proper flow control, it avoids the repeated allocation of the excluded keys array, and avoids the lookup from that array when o[k] is truthy. These things are much easier to spot when the code is laid out sensibly.
1

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

Comments

1

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);

5 Comments

rest with objects.
@NinaScholz: It will only fail in browsers that haven't implemented that feature yet. Were you unaware of this feature?
Where did @NinaScholz go?
i know this feature, but it is not always implemented yet.
@NinaScholz: No features are always implemented. That's why we have transpilers.
1

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; }

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.