Data transformation horizontal to vertical javascript - javascript

I need help in transforming data in a particular way to plot a graph. The data which I get from API is a different format. Please guide me on how to transform it
const demo = [
{
label: 'ABC',
vMini: 28,
vMaxi: 56,
dMini: 2,
dMaxi: 50,
},
{
label: 'BCD',
vMini: 2,
vMaxi: 56,
dMini: 3,
dMaxi: 50,
},
];
end result which i want is
[
{
section: "vMini",
"ABC": 28,
"BCD": 2,
},
{
section: "vMaxi",
"ABC": 56,
"BCD": 56
}
{
section: "dMini",
"ABC": 2,
"BCD": 3,
},
{
section: "dMaxi",
"ABC": 50,
"BCD": 50
}
]
I have started working on it and got confused with second loop.
for (let i = 0; i < demo.length; i += 1) {
for (let j in demo[i]) {
if (j === 'label') {
}
}
}

This one is a bit tricky with the way the data is structured, but you should be able to do this with array.reduce, like so:
const demo = [{label:"ABC",vMini:28,vMaxi:56,dMini:2,dMaxi:50},{label:"BCD",vMini:2,vMaxi:56,dMini:3,dMaxi:50}];
// get array of keys, and create a new object for each one except label
// ["label", "vMini", "vMaxi", "dMini", "dMaxi"]
let results = Object.keys(demo[0]).reduce((res, key) => {
if (key === "label") { return res; }
else {
// for each item in demo, create a key for the label and grab the key's value
let newObj = demo.reduce((_res, obj) => {
_res[obj.label] = obj[key];
return _res;
}, {section: key})
// push the new object into the results array
res.push(newObj);
}
return res;
}, [])
console.log(results);

Using reduce() and Map()
const demo = [{label:"ABC",vMini:28,vMaxi:56,dMini:2,dMaxi:50},{label:"BCD",vMini:2,vMaxi:56,dMini:3,dMaxi:50}];
const resMap = demo.reduce((a, v) => {
let label = v.label
for (let k in v) {
if (k == 'label') continue
a.has(k) || a.set(k, { section: k })
let o = a.get(k)
o[label] = v[k]
}
return a
}, new Map())
const resArr = [...resMap.values()]
console.log(resArr)

Related

How to invert the structure of nested array of objects in Javascript?

I currently have an array that has the following structure:
data = [
{
time: 100,
info: [{
name: "thing1",
count: 3
}, {
name: "thing2",
count: 2
}, {
}]
},
{
time: 1000,
info: [{
name: "thing1",
count: 7
}, {
name: "thing2",
count: 0
}, {
}]
}
];
But I would like to restructure the array to get something like this:
data = [
{
name: "thing1",
info: [{
time: 100,
count: 3
}, {
time: 1000,
count: 7
}, {
}]
},
{
name: "thing2",
info: [{
time: 100,
count: 2
}, {
time: 1000,
count: 0
}, {
}]
}
];
So basically the key would have to be switched from time to name, but the question is how. From other posts I have gathered that using the map function might work, but since other posts had examples to and from different structures I am still not sure how to use this.
There are a number of ways to achieve this however, the key idea will be to perform a nested looping of both data items and their (nested) info items. Doing that allows your algorithm to "visit" and "map" each piece of input data, to a corresponding value in the resulting array.
One way to express that would be to use nested calls to Array#reduce() to first obtaining a mapping of:
name -> {time,count}
That resulting mapping would then be passed to a call to Object.values() to transform the values of that mapping to the required array.
The inner workings of this mapping process are summarized in the documentation below:
const data=[{time:100,info:[{name:"thing1",count:3},{name:"thing2",count:2},{}]},{time:1e3,info:[{name:"thing1",count:7},{name:"thing2",count:0},{}]}];
const result =
/* Obtain array of values from outerMap reduce result */
Object.values(
/* Iterate array of data items by reduce to obtain mapping of
info.name to { time, count} value type */
data.reduce((outerMap, item) =>
/* Iterate inner info array of current item to compound
mapping of info.name to { time, count} value types */
item.info.reduce((innerMap, infoItem) => {
if(!infoItem.name) {
return innerMap
}
/* Fetch or insert new { name, info } value for result
array */
const nameInfo = innerMap[ infoItem.name ] || {
name : infoItem.name, info : []
};
/* Add { time, count } value to info array of current
{ name, info } item */
nameInfo.info.push({ count : infoItem.count, time : item.time })
/* Compound updated nameInfo into outer mapping */
return { ...innerMap, [ infoItem.name] : nameInfo }
}, outerMap),
{})
)
console.log(result)
Hope that helps!
The approach I would take would be to use an intermediate mapping object and then create the new array from that.
const data = [{time: 100, info: [{name: "thing1", count: 3}, {name: "thing2", count: 2}, {}]}, {time: 1e3, info: [{name: "thing1", count: 7}, {name: "thing2", count: 0}, {}]} ];
const infoByName = {};
// first loop through and add entries based on the name
// in the info list of each data entry. If any info entry
// is empty ignore it
data.forEach(entry => {
if (entry.info) {
entry.info.forEach(info => {
if (info.name !== undefined) {
if (!infoByName[info.name]) {
infoByName[info.name] = [];
}
infoByName[info.name].push({
time: entry.time,
count: info.count
});
}
});
}
});
// Now build the resulting list, where name is entry
// identifier
const keys = Object.keys(infoByName);
const newData = keys.map(key => {
return {
name: key,
info: infoByName[key]
};
})
// newData is the resulting list
console.log(newData);
Well, the other guy posted a much more elegant solution, but I ground this one out, so I figured may as well post it. :)
var data = [
{
time: 100,
info: [{
name: "thing1",
count: 3
}, {
name: "thing2",
count: 2
}, {
}]
},
{
time: 1000,
info: [{
name: "thing1",
count: 7
}, {
name: "thing2",
count: 0
}, {
}]
}
];
var newArr = [];
const objInArray = (o, a) => {
for (var i=0; i < a.length; i += 1) {
if (a[i].name === o)
return true;
}
return false;
}
const getIndex = (o, a) => {
for (var i=0; i < a.length; i += 1) {
if (a[i].name === o) {
return i;
}
}
return false;
}
const getInfoObj = (t, c) => {
let tmpObj = {};
tmpObj.count = c;
tmpObj.time = t;
return tmpObj;
}
for (var i=0; i < data.length; i += 1) {
let t = data[i].time;
for (var p in data[i].info) {
if ("name" in data[i].info[p]) {
if (objInArray(data[i].info[p].name, newArr)) {
let idx = getIndex(data[i].info[p].name, newArr);
let newInfoObj = getInfoObj(t, data[i].info[p].count);
newArr[idx].info.push(newInfoObj);
} else {
let newObj = {};
newObj.name = data[i].info[p].name;
let newInfo = [];
let newInfoObj = getInfoObj(t, data[i].info[p].count);
newInfo.push(newInfoObj);
newObj.info = newInfo;
newArr.push(newObj);
}}
}
}
console.log(newArr);
try to use Object.keys() to get the key

Array of objects how do i check for deeply nested text string duplicates & remove from array?

I have an array of objects
Deep inside those objects is a text string
I want to check if other objects in the same array have the same text string / are duplicates.
Then i need a new array with those duplicates removed.
I thought this would be quite simple but it's been testing my intellect for two days now.
const arr = [
{..obj 1}
{..obj 2}
{..obj 3}
{
id: 4,
uid: 24872-2847-249249892842,
tags: ['some', 'stuff'],
type: "blogpage",
href: "https://link-to-stuff",
first_publication_date: "2020-02-12T16:05:04+0000",
last_publication_date: "2020-02-18T21:52:06+0000",
data: {
...some stuff
heading: [
{ type: "heading1", text: "Here Is My Text I Need To Check Duplicates
Of"}
]
}
}
{..obj 5}
{..obj 6}
{..obj 7}
{..obj 8}
{..obj 9}
{..obj 10}
]
I figured something like:
filterOutDuplicates = (blogIndexContent) => {
let arr = blogIndexContent.pages;
let results = [];
arr.map(each => {
if (!results || !results.length) {
results.push(each);
} else {
for (let i = 0; i < results.length; i++) {
const headline = results[i].data.heading[0].text;
if (headline === each.data.heading[0].text) {
return;
} else {
return results.push(each);
}
}
}
})
console.log('Results :', results); // <-- this just gives me the same 9 blog stories again, no duplicates removed.
}
What am i doing wrong guys?
If you dont mind using lodash, it could be easily solved using _.uniqBy
const withoutDups = _.uniqBy(arr, 'data.heading[0].text')
Try this
const arr = [
{
id: 4,
data: {
heading: [
{
type: "heading1",
text: "Here Is My Text I Need To Check Duplicates Of"
}
]
}
},
{
id: 5,
data: {
heading: [
{
type: "heading1",
text: "Here Is My Text I Need To Check Duplicates Of"
}
]
}
},
{
id: 6,
data: {
heading: [
{
type: "heading1",
text: "Not Duplicates"
}
]
}
}
];
const withoutDuplicates = arr.reduce(
(prev, curr) =>
prev
.map(d => d["data"]["heading"][0]["text"])
.includes(curr["data"]["heading"][0]["text"])
? [curr]
: [...prev, curr],
[]
);
console.log(withoutDuplicates);
Slight changes to your code
1) remove using map, have loop over array.
2) Build the uniq object with keys. (Here headline is what we want)
3) Add to results array only when key is not in uniq
let arr = blogIndexContent.pages;
let results = [];
const uniq = {};
for (let i = 0; i < arr.length; i++) {
const headline = arr[i].data.heading[0].text;
if (!(headline in uniq)) {
results.push(each);
uniq[each] = 1;
}
}
console.log("Results :", results);
This should work for you:
filterOutDuplicates = blogIndexContent => {
let arr = blogIndexContent.pages
const result = []
arr.forEach(each => {
if (result.length === 0) {
result.push(each)
}
else {
const headline = each.data.heading[0].text
let found = false
for (let i = 0; i < result.length; i++) {
if (result[i].data.heading[0].text === headline) {
found = true
break
}
}
if (!found) {
result.push(each)
}
}
})
console.log('Results :', results)
}

how to count duplicate values object to be a value of object

how to count the value of object in new object values
lets say that i have json like this :
let data = [{
no: 3,
name: 'drink'
},
{
no: 90,
name: 'eat'
},
{
no: 20,
name: 'swim'
}
];
if i have the user pick no in arrays : [3,3,3,3,3,3,3,3,3,3,3,90,20,20,20,20]
so the output should be an array
[
{
num: 3,
total: 11
},
{
num: 90,
total: 1
},
{
num:20,
total: 4
}
];
I would like to know how to do this with a for/of loop
Here is the code I've attempted:
let obj = [];
for (i of arr){
for (j of data){
let innerObj={};
innerObj.num = i
obj.push(innerObj)
}
}
const data = [{"no":3,"name":"drink"},{"no":90,"name":"eat"},{"no":20,"name":"swim"}];
const arr = [3,3,3,3,3,3,3,3,3,3,3,20,20,20,20,80,80];
const lookup = {};
// Loop over the duplicate array and create an
// object that contains the totals
for (let el of arr) {
// If the key doesn't exist set it to zero,
// otherwise add 1 to it
lookup[el] = (lookup[el] || 0) + 1;
}
const out = [];
// Then loop over the data updating the objects
// with the totals found in the lookup object
for (let obj of data) {
lookup[obj.no] && out.push({
no: obj.no,
total: lookup[obj.no]
});
}
document.querySelector('#lookup').textContent = JSON.stringify(lookup, null, 2);
document.querySelector('#out').textContent = JSON.stringify(out, null, 2);
<h3>Lookup output</h3>
<pre id="lookup"></pre>
<h3>Main output</h3>
<pre id="out"></pre>
Perhaps something like this? You can map the existing data array and attach filtered array counts to each array object.
let data = [
{
no: 3,
name: 'drink'
},
{
no:90,
name: 'eat'
},
{
no:20,
name: 'swim'
}
]
const test = [3,3,3,3,3,3,3,3,3,3,3,90,20,20,20,20]
const result = data.map((item) => {
return {
num: item.no,
total: test.filter(i => i === item.no).length // filters number array and then checks length
}
})
You can check next approach using a single for/of loop. But first I have to create a Set with valid ids, so I can discard noise data from the test array:
const data = [
{no: 3, name: 'drink'},
{no: 90, name: 'eat'},
{no: 20, name: 'swim'}
];
const userArr = [3,3,3,3,3,3,3,3,7,7,9,9,3,3,3,90,20,20,20,20];
let ids = new Set(data.map(x => x.no));
let newArr = [];
for (i of userArr)
{
let found = newArr.findIndex(x => x.num === i)
if (found >= 0)
newArr[found].total += 1;
else
ids.has(i) && newArr.push({num: i, total: 1});
}
console.log(newArr);

Getting values of nested objects and arrays - with plunker

I have two arrays, with nested objects, downloaded as part of calls to API endpoints, one (preview) has just numbers.
Example:
[{
obj1:[1, 2],
obj2:[3, 4]
}]
I had to make a second call to another endpoint, to get a list of IDs with strings
Example:
[{
obj1:[{
id:1,
name:'string_name1'
}, {
id:2,
name:'string_name2'
}]
}, {
obj2:[{
id:3,
name:'string_name3'
}, {
id:4,
name:'string_name4'
}]
}];
I need to match the IDs to the first array of objects numbers, so I have strings/text values to display on my web page
I have 2 functions
The first one, pulls the numbers from the preview array and pushes them to my own editable array that I will use to display on the page
This is the array before function runs
objName = [['obj1'], ['obj2']];
This is the first function, matches the names in preview to the names in my array and pushes values
setNumbers(){
for(let i = 0; i < this.objName.length; i++){
for(var name in this.preview[0]) {
if (name == this.objName[i][0]){
for(var val in this.preview[0][name]) {
this.objName[i].push(this.preview[0][name][val])
}
}
}
}
this.setStrings()
}
The second matches the IDs in fields to the numbers in objName and replaces with the string value
public setStrings(){
let feildId, feildName;
for(let i = 0; i < this.fields.length; i++){
var obj = this.fields[i]
for(var name in obj) {
if(this.objName[i][0] == name){
for(let j = 0; j < obj[name].length; j++){
feildId = obj[name][j].id
feildName = obj[name][j].name;
for(let x = 0; x < this.objName[i].length; x++){
if (this.objName[i][x] == feildId){
var index = this.objName[i].indexOf(feildId)
if (index !== -1) {
this.objName[i][index] = feildName;
}
}
}
}
}
}
}
console.log(this.objName)
}
The objName array, for output, ends up looking like:
[['obj1', 'string_name1', 'string_name2'], ['obj2', 'string_name3', 'string_name4']]
It works, but makes my eyes hurt, there must be an easier cleaner way of doing this?
Plunker link:
https://plnkr.co/edit/KBDu3ZehHl04er6eut6r?p=preview
Your data structures are not ideal for this kind of transformation. For instance, it would have been better if the display strings could be addressed directly given an "obj"-property and array index, without having to iterate through arrays.
Anyway, using the existing structure, you can still improve by using array functions, such as find and map:
class App {
constructor(preview, objName, fields) {
this.preview = preview;
this.objName = objName;
this.fields = fields;
this.setNumbers();
}
setNumbers() {
this.objName = this.objName.map( arr => arr.concat(this.preview[0][arr[0]]) );
this.setStrings();
}
setStrings() {
this.objName = this.objName.map( arr =>
[arr[0]].concat(arr.slice(1).map( val =>
this.fields.find( field => arr[0] in field )[arr[0]]
.find( item => item.id === val ).name
))
);
console.log(this.objName);
}
}
var objName = [['obj1'], ['obj2']],
preview = [{
obj1: [1, 2],
obj2: [3, 4]
}],
fields = [{
obj1:[{
id:1,
name:'string_name1'
}, {
id:2,
name:'string_name2'
}]
}, {
obj2:[{
id:3,
name:'string_name3'
}, {
id:4,
name:'string_name4'
}]
}];
new App(preview, objName, fields);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Note that this code assumes all searches lead to matches. If this is not your case, you'll have to add some code to define which values should be returned in case of non-matching references.
Here is such a variant of the code:
class App {
constructor(preview, objName, fields) {
this.preview = preview;
this.objName = objName;
this.fields = fields;
this.setNumbers();
}
setNumbers() {
this.objName = this.objName.map( arr =>
arr[0] in this.preview[0]
? arr.concat(this.preview[0][arr[0]])
: arr
);
this.setStrings();
}
setStrings() {
this.objName = this.objName.map( arr =>
[arr[0]].concat(arr.slice(1).map( val => {
let find = this.fields.find( field => arr[0] in field );
if (find) find = find[arr[0]].find( item => item.id === val );
return find ? find.name : val;
}))
);
console.log(this.objName);
}
}
var objName = [['obj1'], ['obj2'], ['obj3']],
preview = [{
obj1: [1, 2],
obj2: [3, 4, 5],
}],
fields = [{
obj1:[{
id:1,
name:'string_name1'
}, {
id:2,
name:'string_name2'
}]
}, {
obj2:[{
id:3,
name:'string_name3'
}, {
id:4,
name:'string_name4'
}]
}];
new App(preview, objName, fields);
.as-console-wrapper { max-height: 100% !important; top: 0; }
It's easier and cleaner to do this if you break it down into smaller pieces:
let objsToMap = [{
obj1: [1, 2, 7],
obj2: [3, 4],
obj3: [1, 2]
}]
let objValues = [{
obj1: [{
id: 1,
name: 'string_name1'
}, {
id: 2,
name: 'string_name2'
}]
}, {
obj2: [{
id: 3,
name: 'string_name3'
}, {
id: 4,
name: 'string_name4'
}]
}];
function findValueForId(objDef, id) {
let idKeyMap = objDef.find(item => item.id === id);
return idKeyMap ? idKeyMap.name : null;
}
function findObjectValues(valueMapping, key) {
let objectWithObjectValues = valueMapping.find(item => key in item);
return objectWithObjectValues ? objectWithObjectValues[key] : null;
}
// returns an array containing key followed by the values corresponding to the specified ids
function lookupObject(key, ids, valueMapping) {
let objDef = findObjectValues(valueMapping, key) || [];
let valuesForIds = ids.map(id => findValueForId(objDef, id));
let valuesWithoutBlanks = valuesForIds.filter(value => value);
return [key].concat(valuesWithoutBlanks);
}
let result = Object.entries(objsToMap[0]).map(([k, v]) => lookupObject(k, v, objValues));
console.log(result);
You'll notice that this approach uses .find() in two places because your second data structure nests everything into arrays instead of having direct property references. This isn't very good because it's not good for performance and makes the code more convoluted than it has to be.
Another option is to rearrange the second array before consuming it, so that it's like this:
let objValues = {
obj1: {
'1': 'string_name1',
'2': 'string_name2'
},
obj2: {
'3': 'string_name3',
'4': 'string_name4'
}
};
Here's how you could do that:
let objsToMap = [{
obj1: [1, 2, 7],
obj2: [3, 4],
obj3: [1, 2]
}]
let objValuesRaw = [{
obj1: [{
id: 1,
name: 'string_name1'
}, {
id: 2,
name: 'string_name2'
}]
}, {
obj2: [{
id: 3,
name: 'string_name3'
}, {
id: 4,
name: 'string_name4'
}]
}];
function cleanupObjDef(objDef) {
return objDef.reduce(function(acc, el) {
acc[el.id] = el.name;
return acc;
}, {});
}
function cleanupObjValues(objValues) {
let allCombined = Object.assign({}, ...objValues);
return Object.entries(allCombined).reduce(function (acc, [k, v]) {
acc[k] = cleanupObjDef(v);
return acc;
}, {});
}
// returns an array containing key followed by the values corresponding to the specified ids
function lookupObject(key, ids, valueMapping) {
let objDef = valueMapping[key] || {};
let valuesForIds = ids.map(id => objDef[id]);
let valuesWithoutBlanks = valuesForIds.filter(value => value);
return [key].concat(valuesWithoutBlanks);
}
let objValues = cleanupObjValues(objValuesRaw);
let result = Object.keys(objsToMap[0]).map(key => lookupObject(key, objsToMap[0][key], objValues));
console.log(result);

lodash convert array of objects to single array of keys and multiple array of values

I need to transmit some data, that has too many key-value pairs.
As the keys are similar, I dont want to transmit them with each object.
Consider I have the following data:
[
{
x:11,
y:12
},{
x:21,
y:22
},{
x:31,
y:32
},{
x:41,
y:42
}
];
And I need the final output as
[ [x,y],[[11,12],[21,22],[31,32],[41,42]] ] OR
[ [x,y],[11,12],[21,22],[31,32],[41,42] ]
On the other end, I should be able to convert back to its original form.
It would be great if it can handle an additional key in some of the objects
I think I have seen lodash or underscore function for something close/similar to this, but I'm not able to find it right now.
NOTE: I don't know what the keys will be
Lodash v4.17.1
modify original
var modifiedOriginal = _.chain(original)
.map(_.keys)
.flatten()
.uniq()
.thru(function(header){
return _.concat(
[header],
_.map(original, function(item) {
return _.chain(item)
.defaults(_.zipObject(
header,
_.times(_.size(header), _.constant(undefined))
))
.pick(header)
.values()
.value()
})
);
})
.value();
modified back to original (keys order is not
guarantee)
var backToOriginal = _.map(_.tail(modified), function(item) {
return _.chain(_.head(modified))
.zipObject(item)
.transform(function(result, val, key) {
if (!_.isUndefined(val)) {
result[key] = val;
}
})
.value();
});
JSFiddle code https://jsfiddle.net/wa8kaL5g/1/
Using Array#reduce
var arr = [{
x: 11,
y: 12
}, {
x: 21,
y: 22
}, {
x: 31,
y: 32
}, {
x: 41,
y: 42
}];
var keys = Object.keys(arr[0]);
var op = arr.reduce(function(a, b) {
var arr = keys.reduce(function(x, y) {
return x.concat([b[y]]);
}, [])
return a.concat([arr]);
}, [keys]); //If all the objects are having identical keys!
console.log(JSON.stringify(op));
A little more verbose way of doing it:
[Edit: added the function to convert it back]
function convert(arr) {
var retArr = [ [/* keys (retArr[0]) */], [/* values (retArr[1]) */] ]
arr.forEach(function(obj){
// create new array for new sets of values
retArr[1].push([])
// put all of the keys in the correct array
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
// does the key exist in the array yet?
if (retArr[0].indexOf(key) === -1) {
retArr[0].push(key)
}
// get last index of retArr[1] and push on the values
retArr[1][retArr[1].length - 1].push(obj[key])
}
}
})
return retArr
}
function reConvert(arr) {
var retArr = []
var keys = arr[0]
arr[1].forEach(function(itemArr){
var obj = {}
itemArr.forEach(function(item, i){
obj[keys[i]] = item
})
retArr.push(obj)
})
return retArr
}
var objArr = [
{
x:11,
y:12
},{
x:21,
y:22
},{
x:31,
y:32
},{
x:41,
y:42
}
]
var arrFromObj = convert(objArr)
var objFromArr = reConvert(arrFromObj)
console.log(arrFromObj)
console.log(objFromArr)
A solution using Underscore.
First work out what the keys are:
var keys = _.chain(data)
.map(_.keys)
.flatten()
.uniq()
.value();
Then map across the data to pick out the value for each key:
var result = [
keys,
_.map(data, item => _.map(keys, key => item[key]))
];
and back again:
var thereAndBackAgain = _.map(result[1], item => _.omit(_.object(result[0], item), _.isUndefined));
Lodash's version of object is zipObject and omit using a predicate is omitBy:
var thereAndBackAgain = _.map(result[1], item => _.omitBy(_.zipObject(result[0], item), _.isUndefined));
var data = [
{
x:11,
y:12,
aa: 9
},{
x:21,
y:22
},{
x:31,
y:32,
z: 0
},{
x:41,
y:42
}
];
var keys = _.chain(data)
.map(_.keys)
.flatten()
.uniq()
.value();
var result = [
keys,
_.map(data, item => _.map(keys, key => item[key]))
];
var thereAndBackAgain = _.map(result[1], item => _.omit(_.object(result[0], item), _.isUndefined));
console.log(result)
console.log(thereAndBackAgain)
<script src="
https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
In ES6 you can do it by reducing it with Object.values(), and Object.keys(). You can restore it using a combination of Array.prototype.map() and Array.prototype.reduce():
const convertStructure = (data) => data.reduce((s, item) => {
s[1].push(Object.values(item));
return s;
}, [Object.keys(data[0]), []]); // all objects should be the same, so we can take the keys from the 1st object
const restoreStructure = ([keys, data]) => data.map((item) => item.reduce((o, v, i) => {
o[keys[i]] = v;
return o;
}, {}));
const data = [{
x: 11,
y: 12
}, {
x: 21,
y: 22
}, {
x: 31,
y: 32
}, {
x: 41,
y: 42
}];
const convertedStructure = convertStructure(data);
console.log('convertedStructure:\n', convertedStructure);
const restoredStructure = restoreStructure(convertedStructure);
console.log('restoredStructure:\n', restoredStructure);

Categories