最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

jquery - Transpose an array of objects in JavaScript - Stack Overflow

programmeradmin1浏览0评论

I am attempting to transpose an array of objects in JavaScript.

I have this array of objects:

var myData = [	
{"shift":"1","date":"01/01/2016/08/00/00","car":"178","truck":"255","bike":"317","moto":"237"},
{"shift":"2","date":"01/01/2016/17/00/00","car":"125","truck":"189","bike":"445","moto":"273"},
{"shift":"3","date":"02/01/2016/08/00/00","car":"140","truck":"219","bike":"328","moto":"412"},
{"shift":"4","date":"02/01/2016/17/00/00","car":"222","truck":"290","bike":"432","moto":"378"},
{"shift":"5","date":"03/01/2016/08/00/00","car":"200","truck":"250","bike":"420","moto":"319"},
{"shift":"6","date":"03/01/2016/17/00/00","car":"230","truck":"220","bike":"310","moto":"413"},
{"shift":"7","date":"04/01/2016/08/00/00","car":"155","truck":"177","bike":"377","moto":"180"},
{"shift":"8","date":"04/01/2016/17/00/00","car":"179","truck":"203","bike":"405","moto":"222"},
{"shift":"9","date":"05/01/2016/08/00/00","car":"208","truck":"185","bike":"360","moto":"195"},
{"shift":"10","date":"05/01/2016/17/00/00","car":"150","truck":"290","bike":"315","moto":"280"},
{"shift":"11","date":"06/01/2016/08/00/00","car":"200","truck":"220","bike":"350","moto":"205"},
{"shift":"12","date":"06/01/2016/17/00/00","car":"230","truck":"170","bike":"390","moto":"400"}
];
out = '';
$.each(myData, function(ndx,obj){
	 out += '{';
	 $.each(obj, function(key,val){
   		out += key +':'+ val +',';
   });
   out += '}<br>';
});
$('body').append(out);
<script src=".1.1/jquery.min.js"></script>

I am attempting to transpose an array of objects in JavaScript.

I have this array of objects:

var myData = [	
{"shift":"1","date":"01/01/2016/08/00/00","car":"178","truck":"255","bike":"317","moto":"237"},
{"shift":"2","date":"01/01/2016/17/00/00","car":"125","truck":"189","bike":"445","moto":"273"},
{"shift":"3","date":"02/01/2016/08/00/00","car":"140","truck":"219","bike":"328","moto":"412"},
{"shift":"4","date":"02/01/2016/17/00/00","car":"222","truck":"290","bike":"432","moto":"378"},
{"shift":"5","date":"03/01/2016/08/00/00","car":"200","truck":"250","bike":"420","moto":"319"},
{"shift":"6","date":"03/01/2016/17/00/00","car":"230","truck":"220","bike":"310","moto":"413"},
{"shift":"7","date":"04/01/2016/08/00/00","car":"155","truck":"177","bike":"377","moto":"180"},
{"shift":"8","date":"04/01/2016/17/00/00","car":"179","truck":"203","bike":"405","moto":"222"},
{"shift":"9","date":"05/01/2016/08/00/00","car":"208","truck":"185","bike":"360","moto":"195"},
{"shift":"10","date":"05/01/2016/17/00/00","car":"150","truck":"290","bike":"315","moto":"280"},
{"shift":"11","date":"06/01/2016/08/00/00","car":"200","truck":"220","bike":"350","moto":"205"},
{"shift":"12","date":"06/01/2016/17/00/00","car":"230","truck":"170","bike":"390","moto":"400"}
];
out = '';
$.each(myData, function(ndx,obj){
	 out += '{';
	 $.each(obj, function(key,val){
   		out += key +':'+ val +',';
   });
   out += '}<br>';
});
$('body').append(out);
<script src="https://ajax.googleapis./ajax/libs/jquery/2.1.1/jquery.min.js"></script>

I need it to be transposed, so that (when transposed) it looks like this (note: I did this manually, I need to do it programmatically - and print it as above):

var myData2 = [
{"shift":"1","shift":"2","shift":"3","shift":"4","shift":"5","shift":"6","shift":"7","shift":"8","shift":"9","shift":"10","shift":"11","shift":"12"},
{"date":"01/01/2016/08/00/00","date":"01/01/2016/17/00/00","date":"02/01/2016/08/00/00","date":"02/01/2016/17/00/00","date":"03/01/2016/08/00/00","date":"03/01/2016/17/00/00","date":"04/01/2016/08/00/00","date":"04/01/2016/17/00/00","date":"05/01/2016/08/00/00","date":"05/01/2016/17/00/00","date":"06/01/2016/08/00/00","date":"06/01/2016/17/00/00"},
{"car":"178","car":"125","car":"140","car":"222","car":"200","car":"230","car":"155","car":"179","car":"208","car":"150","car":"200","car":"230"},
{"truck":"255","truck":"189","truck":"219","truck":"290","truck":"250","truck":"220","truck":"177","truck":"203","truck":"185","truck":"290","truck":"220","truck":"170"},
{"bike":"317","bike":"445","bike":"328","bike":"432","bike":"420","bike":"310","bike":"377","bike":"405","bike":"360","bike":"315","bike":"350","bike":"390"},
{"moto":"237","moto":"273","moto":"412","moto":"378","moto":"319","moto":"413","moto":"180","moto":"222","moto":"195","moto":"280","moto":"205","moto":"400"}
];

jsFiddle

Share Improve this question edited Dec 27, 2019 at 16:17 halfer 20.4k19 gold badges109 silver badges202 bronze badges asked Mar 18, 2017 at 22:57 crashwapcrashwap 3,0724 gold badges36 silver badges65 bronze badges 9
  • 10 objects cannot have duplicate property names. – trincot Commented Mar 18, 2017 at 22:58
  • Thanks gang, I should have seen the fundamental problem with where I was going. This was for a d3.js application where the input data csv file has columnar data, so it is impossible to represent correctly. I was attempting to transpose the data to a row-oriented format, but clearly I've been working at this too long. Thanks for the ideas. – crashwap Commented Mar 18, 2017 at 23:30
  • If you need it for d3 @trincot's answer and mine will both work. @trincot's more elegant, but takes the keys from first element in your current data and assumes they all have the same structure. It will error if they're not. Mine won't, but that doesn't help you much, as d3 won't draw the expected chart (if any object is different). So you should go with trincots answer. If you want to track objects with variable structure you must have a separate array of all properties and use shift as index/tracker. – tao Commented Mar 18, 2017 at 23:35
  • Eh, @AndreiGheorghiu, my solution will not error if there are fewer or more properties in other rows, it will just skip properties that are not in the first object, and fill gaps with undefined when properties are missing in the other objects. – trincot Commented Mar 18, 2017 at 23:39
  • @trincot I'm afraid it will error if any object has a property the first one doesn't. However, that's hardly likely to happen in this case. Considering it es from a database, I think your answer is solid and should be the accepted one. – tao Commented Mar 18, 2017 at 23:41
 |  Show 4 more ments

3 Answers 3

Reset to default 9

Your desired result has duplicate properties, which is not allowed.

Instead, you could turn that inner structure into an array with just the values, and assign that to the property. Like this:

{
  "shift": ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12"],
  "date": ["01/01/2016/08/00/00", "01/01/2016/17/00/00", // ...etc
}

You can use this ES6 code for that transformation:

const result = Object.assign(...Object.keys(myData[0]).map( key =>
    ({ [key]: myData.map( o => o[key] ) })
));

var myData = [	
{"shift":"1","date":"01/01/2016/08/00/00","car":"178","truck":"255","bike":"317","moto":"237"},
{"shift":"2","date":"01/01/2016/17/00/00","car":"125","truck":"189","bike":"445","moto":"273"},
{"shift":"3","date":"02/01/2016/08/00/00","car":"140","truck":"219","bike":"328","moto":"412"},
{"shift":"4","date":"02/01/2016/17/00/00","car":"222","truck":"290","bike":"432","moto":"378"},
{"shift":"5","date":"03/01/2016/08/00/00","car":"200","truck":"250","bike":"420","moto":"319"},
{"shift":"6","date":"03/01/2016/17/00/00","car":"230","truck":"220","bike":"310","moto":"413"},
{"shift":"7","date":"04/01/2016/08/00/00","car":"155","truck":"177","bike":"377","moto":"180"},
{"shift":"8","date":"04/01/2016/17/00/00","car":"179","truck":"203","bike":"405","moto":"222"},
{"shift":"9","date":"05/01/2016/08/00/00","car":"208","truck":"185","bike":"360","moto":"195"},
{"shift":"10","date":"05/01/2016/17/00/00","car":"150","truck":"290","bike":"315","moto":"280"},
{"shift":"11","date":"06/01/2016/08/00/00","car":"200","truck":"220","bike":"350","moto":"205"},
{"shift":"12","date":"06/01/2016/17/00/00","car":"230","truck":"170","bike":"390","moto":"400"}
];

const result = Object.assign(...Object.keys(myData[0]).map( key =>
    ({ [key]: myData.map( o => o[key] ) })
));

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

In case you cannot guarantee that all the objects have the same properties, but you want all occurring properties to be represented in the output, then you could first gather all properties that occur and keep the unique keys (with a Set):

const result = Object.assign(...Array.from(
    new Set(myData.reduce((keys, o) => keys.concat(Object.keys(o)), [] )),
    key => ({ [key]: myData.map( o => o[key] ) })
));

var myData = [	
{"shift":"1","date":"01/01/2016/08/00/00","car":"178","truck":"255","bike":"317","moto":"237"},
{"shift":"2","date":"01/01/2016/17/00/00","car":"125","truck":"189","bike":"445","moto":"273"},
{"shift":"3","date":"02/01/2016/08/00/00","car":"140","truck":"219","bike":"328","moto":"412"},
{"shift":"4","date":"02/01/2016/17/00/00","car":"222","truck":"290","bike":"432","moto":"378"},
{"shift":"5","date":"03/01/2016/08/00/00","car":"200","truck":"250","bike":"420","moto":"319"},
{"shift":"6","date":"03/01/2016/17/00/00","car":"230","truck":"220","bike":"310","moto":"413"},
{"shift":"7","date":"04/01/2016/08/00/00","car":"155","truck":"177","bike":"377","moto":"180"},
{"shift":"8","date":"04/01/2016/17/00/00","car":"179","truck":"203","bike":"405","moto":"222"},
{"shift":"9","date":"05/01/2016/08/00/00","car":"208","truck":"185","bike":"360","moto":"195"},
{"shift":"10","date":"05/01/2016/17/00/00","car":"150","truck":"290","bike":"315","moto":"280"},
{"shift":"11","date":"06/01/2016/08/00/00","car":"200","truck":"220","bike":"350","moto":"205"},
{"shift":"12","date":"06/01/2016/17/00/00","car":"230","truck":"170","bike":"390","moto":"400"}
];

const result = Object.assign(...Array.from(
    new Set(myData.reduce((keys, o) => keys.concat(Object.keys(o)), [] )),
    key => ({ [key]: myData.map( o => o[key] ) })
));

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

Since objects cannot have repeated properties, I remend accumulating values with the same property name as arrays:

function transpose(data) {
  let result = {};
  for (let row of data) {
    for (let [key, value] of Object.entries(row)) {
      result[key] = result[key] || [];
      result[key].push(value); 
    }
  }
  return result;
}

// Example:
let data = [	
  {"shift": 1, "date": "01/01/2016/08/00/00"},
  {"shift": 2, "date": "01/01/2016/17/00/00"},
  {"shift": 3, "date": "02/01/2016/08/00/00"}
];

console.log(transpose(data)); // {"shift": [1, 2, 3], "date": [...]}

For a safe typescript-friendly equivalent to trincot's oneliner using the new Object.fromEntries function:

const result = Object.fromEntries(Object.keys(myData[0]).map(key => [key, myData.map(o => o[key])]))

Breaking this down a bit:

const keys = Object.keys(myData[0]);
const entries = keys.map(key => [key, myData.map(o => o[key])]);
const result = Object.fromEntries(entries)

The original code using Object.assign and the spread operator will throw an error if there are no keys, and so the TS piler rejects the code with the error "Expected at least 1 arguments, but got 0 or more.". The new code will result in {} if there are no keys.

发布评论

评论列表(0)

  1. 暂无评论