I'm learning to manipulate JSON data and I am stuck trying to figure out how to cajole the following JSON into what I want as shown below:
Any pointers to function/terms/concepts that I should learn for this sort of problem would be greatly appreciated! Thanks
JSON object
{
car: 1,
van: 5,
cat: 99999999999999999999999
}
Desired outcome:
items: [
{ "type": "car", "value": "1"},
{ "type": "van", "value": "5"},
{ "type": "cat", "value": "99999999999999999999999"}
]
You can use a combination of Object.entries and Array.prototype.map:
const obj = { car: 1, van: 5, cat: 99999999999999999999999 };
let list = Object.entries(obj) // [["car",1],["van",5],["cat",99999999999999999999999]]
.map(x => ({ type: x[0], value: x[1] }));
console.log(list);
Or, with some destructuring:
const obj = { car: 1, van: 5, cat: 99999999999999999999999 };
let list = Object.entries(obj)
.map(([type, value]) => ({ type, value }));
console.log(list);
The callback to map:
([type, value]) => ({ type, value })
Expects an array as parameter: [type, value]. The first value in that array is assigned to type, the second one to value.
Then we use a shorthand form to set these values in our returned object:
=> ({ type, value })
I'm a beginner. I tried to solve the problem and this is the best I can come up with, tested in Node.js 10.
const obj = {"car": 1, "van": 5, "cat": 999999}
const items = []
for (let key in obj) {
items.push({"type": key, "value": obj[key]})
}
console.log(items)
One thing I am slightly confused about is the difference between for..in vs for..of, I'm currently looking into it.
Object.keys will return:
['car', 'van', 'cat'];
On this array you can use Array's map function which creates a new array with the results of calling a provided function on every element in the calling array.
var a = {
car: 1,
van: 5,
cat: 99999999999999999999999
}
m = Object.keys(a).map((v)=>{
return {
type: v,
value: a[v]
}
})
console.log(m);
#GustavMahler hope you understand. To learn more about array functions you should look map, reduce and filter.
This one uses object.keys
let js = {car:1, van:5, cat:9999}
Object.keys(js).map( x => ({type: x, value: js[x] }) )
[ { type: 'car', value: 1 },
{ type: 'van', value: 5 },
{ type: 'cat', value: 9999 } ]
Related
How to optimize this transformation from array to object with specific key?
I have this array that inside has other arrays, and I wanted to turn this array into an array of objects. I would like to do this without using this index, and the object would like it to have these specific keys. I'm new to javascript and I would like to know if the way I did it was the best way, or if I can use a Map or Reduce to do what I want.
const listaCesar = [["cesar", "1", 1], ["thiago", "2", 2], ["giuseppe", "3", 3]]
const dict = []
listaCesar.forEach(item => dict.push({name: item[0], id: item[1], age: item[2]}))
console.log(dict)
This code works and gives me the expected result, but I don't know if I did it in the best way
ExpectResult = [{name: "cesar", id: "1", age: "1"}, {name: "thiago", id: "2", age: "2"}, {name: "giuseppe", id: "3", age: "3"}]
your solution is not bad, but I think you want something more "Elegant" so you can reduce your function to something like this:
const dict = listaCesar.map(([name, id, age]) => ({ name, id, age }));
basically with [name, id, age] you are destructuring the inner array and using those same names in the object { name, id, age } you will create a key value object with those name as keys.
const listaCesar = [
["cesar", "1", 1],
["thiago", "2", 2],
["giuseppe", "3", 3]
]
const dict = []
listaCesar.forEach(item => dict.push({
name: item[0],
id: item[1],
age: item[2]
}))
console.log(dict);
console.log('////////////////////////////////////////');
const dict2 = listaCesar.map(([name, id, age]) => ({
name,
id,
age
}));
console.log(dict2);
if you want something more performant to avoid using another structure you can reuse the same array that you have following the same approach, in this case you do not return a new array instead you reuse the same index on the array to put your new object.
const listaCesar = [
["cesar", "1", 1],
["thiago", "2", 2],
["giuseppe", "3", 3]
]
listaCesar.forEach(([name, id, age], i) => {
listaCesar[i] = {
name,
id,
age
}
})
console.log(listaCesar);
One improvement you can do is use an array to keep names of properties and loop over it to create object, so that it becomes extensible
const listaCesar = [
["cesar", "1", 1],
["thiago", "2", 2],
["giuseppe", "3", 3]
]
const props = ['name', 'id', 'age', ]
const dict = listaCesar.map(item => props.reduce((a, b, i) => {
a[b] = item[i]
return a
}, {}))
console.log(dict)
So I've been stumped on this for hours and I can't really figure out an elegant solution to solve this problem. Let's say I have this:
let Fields = {
GAME: [
{ code: '{{GTAV}}', title: { en: "grnti"} },
{ code: '{{GTA5}}', title: { en: "Grand theph " } },
]
};
How can I turn this into a new format that looks like this ?
let Fields = {
tags: [
{ name: 'GAME', tags:[
{ name: 'grnti', value: "{{GTAV}}" },
{ name: 'Grand theph', value: "{{GTA5N}}" }
]},
]};
I tried to create a function to do the job , but for some reason my brain cannot seem to grasp the solution. Any help please !
A simple version of this might look like the following:
const transform = (fields) => ({
mergeTags: Object .entries (fields) .map (([name, innerFields]) => ({
name,
mergeTags: innerFields .map (({code, title: {en}}) => ({name: en, value: code}))
}))
})
const fields = {RECIPIENT: [{code: '{{RECIPIENT.LN}}', title: {en: "name"}}, {code: '{{RECIPIENT.FN}}', title: {en: "first name" }}]}
console .log (transform (fields))
But from your nested mergeTags properties, I'm guessing that there is something recursive going on. If so, we need more information about the input and output structures.
i just threw a nested reduce function together.
const transformed = Object.entries(Fields).reduce((tags, [key, value]) => {
const mergedTags = value.reduce((codes, code) => {
codes.mergeTags.push({name: code.title.en, value: code.code});
return codes;
}, {name: key, mergeTags: []})
tags.mergeTags.push(mergedTags)
return tags;
}, {mergeTags: []})
Does that work for you?
It is hard to tell exactly from your question what you are hoping to accomplish as well as the shape of your data. Based on your question though, you would probably want to use the Object.keys and map functions
let Fields = {
RECIPIENT: [
{ code: '{{RECIPIENT.LN}}', title: { en: "name" } },
{ code: '{{RECIPIENT.FN}}', title: { en: "first name" } },
]
};
// gets the keys of the 'Fields' object(in this case only 'RECIPIENT'
let newFields = Object.keys(Fields)
// each key should create a new object with the 'key' from the original object as the 'name' of the new object
.map(key => ({
name: key,
// 'Fields[key]' gets the array from the 'RECIPIENT' property and then creates a new object from each object in the original array, mapping the 'title.en' property in the original object to 'name' in the new object and 'code' in the original object to 'value' in the new object
mergeTags: Fields[key].map(property => ({
name: property.title.en,
value: property.code
}))
}));
console.log(newFields);
Here's a clean way that may seem a bit like magic, but I'll walk you through what's going on.
let Fields = {
RECIPIENT: [
{ code: '{{RECIPIENT.LN}}', title: { en: "name"} },
{ code: '{{RECIPIENT.FN}}', title: { en: "first name" } },
]
};
const { pipe, fork, map, get } = rubico
const Transformed = pipe([
Object.entries, // { RECIPIENT: [...] } => [['RECIPIENT', [...]]
fork({
mergeTags: map(fork({ // iterate through each entry ['RECIPIENT', [...]]
name: get(0), // name is the item at index 0 of each entry
mergeTags: pipe([
get(1), // mergeTags starts with index 1 of each entry, the array of code+title objects
map(fork({ // iterate through the array of code + title objects and create new objects
name: get('title.en'), // name is title.en of each object
value: get('code'), // value is title.code of each object
})),
]),
})),
}),
])(Fields)
console.log(JSON.stringify(Transformed, null, 2))
<script src="https://unpkg.com/rubico"></script>
Disclaimer: I am the author of rubico
You can examine these methods in depth at the documentation
I am trying to use the map to change the one of the field name , but I see the order of items are getting changed .
var arrOfObj = [
{
name: "test1",
value: "value1"
},
{
name: "test2",
value: "value2"
}
];
function changeKeyName(arr, oldValue, newVal) {
return arr.map(item => {
item[`${newVal}`] = item[`${oldValue}`];
delete item[`${oldValue}`];
return item;
});
}
console.log(changeKeyName(arrOfObj, "name", "type"));
Is there any way I can maintain the order of items along with changing of one of the field names.
O/P:
[{type:"test1",value: "value1"}, {type: "test2", value:"value2"}]
Thanks in advance
You can use Object.entries to take an array of entries, then map them, replacing the old key with the new key when found:
var arrOfObj = [
{
name: "test1",
value: "value1"
},
{
name: "test2",
value: "value2"
}
];
const changeKeyName = (arr, oldKey, newKey) => arr.map(
item => Object.fromEntries(
Object.entries(item).map(
([key, val]) => [key === oldKey ? newKey : key, val]
)
)
);
console.log(changeKeyName(arrOfObj, "name", "type"));
(Contrary to popular belief, object property order is guaranteed by the specification, and has been implemented in all environments for years. But keep in mind that this will only work for keys which aren't array indicies - array indicies like 0 1 2 can't be ordered relative to other properties, they'll always be iterated over first, in ascending numeric order)
That said, code is generally easier to understand if it's written such that property order doesn't matter - if you have code that depends on the properties being in a particular order, I'd recommend refactoring it so that the property order doesn't matter.
map is being used incorrectly here as you should always return a new set of results from that.
You can do this and you can change the order as required:
const arrOfObj = [
{
name: "test1",
value: "value1"
},
{
name: "test2",
value: "value2"
}
];
function changeKeyName(arr, oldValue, newVal) {
return arr.map(({ [oldValue]: val, ...rest }) => {
return {
[newVal]: val,
...rest,
};
});
}
console.log(changeKeyName(arrOfObj, "name", "type"));
I've got two arrays that have multiple objects
[
{
"name":"paul",
"employee_id":"8"
}
]
[
{
"years_at_school": 6,
"department":"Mathematics",
"e_id":"8"
}
]
How can I achieve the following with either ES6 or Lodash?
[
{
"name":"paul",
"employee_id":"8"
"data": {
"years_at_school": 6
"department":"Mathematics",
"e_id":"8"
}
}
]
I can merge but I'm not sure how to create a new child object and merge that in.
Code I've tried:
school_data = _.map(array1, function(obj) {
return _.merge(obj, _.find(array2, {employee_id: obj.e_id}))
})
This merges to a top level array like so (which is not what I want):
{
"name":"paul",
"employee_id":"8"
"years_at_school": 6
"department":"Mathematics",
"e_id":"8"
}
The connector between these two is "employee_id" and "e_id".
It's imperative that it's taken into account that they could be 1000 objects in each array, and that the only way to match these objects up is by "employee_id" and "e_id".
In order to match up employee_id and e_id you should iterate through the first array and create an object keyed to employee_id. Then you can iterate though the second array and add the data to the particular id in question. Here's an example with an extra item added to each array:
let arr1 = [
{
"name":"mark",
"employee_id":"6"
},
{
"name":"paul",
"employee_id":"8"
}
]
let arr2 = [
{
"years_at_school": 6,
"department":"Mathematics",
"e_id":"8"
},
{
"years_at_school": 12,
"department":"Arr",
"e_id":"6"
}
]
// empObj will be keyed to item.employee_id
let empObj = arr1.reduce((obj, item) => {
obj[item.employee_id] = item
return obj
}, {})
// now lookup up id and add data for each object in arr2
arr2.forEach(item=>
empObj[item.e_id].data = item
)
// The values of the object will be an array of your data
let merged = Object.values(empObj)
console.log(merged)
If you perform two nested O(n) loops (map+find), you'll end up with O(n^2) performance. A typical alternative is to create intermediate indexed structures so the whole thing is O(n). A functional approach with lodash:
const _ = require('lodash');
const dataByEmployeeId = _(array2).keyBy('e_id');
const result = array1.map(o => ({...o, data: dataByEmployeeId.get(o.employee_id)}));
Hope this help you:
var mainData = [{
name: "paul",
employee_id: "8"
}];
var secondaryData = [{
years_at_school: 6,
department: "Mathematics",
e_id: "8"
}];
var finalData = mainData.map(function(person, index) {
person.data = secondaryData[index];
return person;
});
Sorry, I've also fixed a missing coma in the second object and changed some other stuff.
With latest Ecmascript versions:
const mainData = [{
name: "paul",
employee_id: "8"
}];
const secondaryData = [{
years_at_school: 6,
department: "Mathematics",
e_id: "8"
}];
// Be careful with spread operator over objects.. it lacks of browser support yet! ..but works fine on latest Chrome version for example (69.0)
const finalData = mainData.map((person, index) => ({ ...person, data: secondaryData[index] }));
Your question suggests that both arrays will always have the same size. It also suggests that you want to put the contents of array2 within the field data of the elements with the same index in array1. If those assumptions are correct, then:
// Array that will receive the extra data
const teachers = [
{ name: "Paul", employee_id: 8 },
{ name: "Mariah", employee_id: 10 }
];
// Array with the additional data
const extraData = [
{ years_at_school: 6, department: "Mathematics", e_id: 8 },
{ years_at_school: 8, department: "Biology", e_id: 10 },
];
// Array.map will iterate through all indices, and gives both the
const merged = teachers.map((teacher, index) => Object.assign({ data: extraData[index] }, teacher));
However, if you want the data to be added to the employee with an "id" matching in both arrays, you need to do the following:
// Create a function to obtain the employee from an ID
const findEmployee = id => extraData.filter(entry => entry.e_id == id);
merged = teachers.map(teacher => {
const employeeData = findEmployee(teacher.employee_id);
if (employeeData.length === 0) {
// Employee not found
throw new Error("Data inconsistency");
}
if (employeeData.length > 1) {
// More than one employee found
throw new Error("Data inconsistency");
}
return Object.assign({ data: employeeData[0] }, teacher);
});
A slightly different approach just using vanilla js map with a loop to match the employee ids and add the data from the second array to the matching object from the first array. My guess is that the answer from #MarkMeyer is probably faster.
const arr1 = [{ "name": "paul", "employee_id": "8" }];
const arr2 = [{ "years_at_school": 6, "department": "Mathematics", "e_id": "8" }];
const results = arr1.map((obj1) => {
for (const obj2 of arr2) {
if (obj2.e_id === obj1.employee_id) {
obj1.data = obj2;
break;
}
}
return obj1;
});
console.log(results);
I'm trying to create a new object (newobj) with new keys and props from a poorly structured existing array of object (arrays?) ex.
[{"product":["1009", "name", "price", "image", "description"]},
{"product":["1004", "name2", "price2", "image2", "description2"]}]
I'm getting result I want but newobj does not update outside of the scope of the forEach method (more than 1 result). My question is what am I not getting ? Is forEach incorrect method with this type obj?
var newobj = {};
Object.keys(oldobj).forEach(function(prop) {
newobj["id"] = Number(oldobj[prop]["product"][0]),
newobj["name"] = oldobj[prop]["product"][1],
newobj["price"] = Number(oldobj[prop]["product"][3]),
newobj["image"] = "url" + oldobj[prop]["product"][0] + ".jpg",
newobj["description"] = oldobj[prop]["product"][2];
// this works
// console.log(JSON.stringify(newobj));
});
// this only updated with one
app.locals.newobj = newobj;
I've also tried mapping (w/ underscore) but I have the same result, I can't access outside scope.
_.each(mappedobj, function(prop) {
_.each(prop["product"][0], function(vals){
newobj["id"] = Number(prop["product"][0]);
console.log(JSON.stringify(newobj));
});
});
If you want all the values from the old object, you need to make newobj an array of objects. You can use .map() to do this transformation.
Object and array destructuring is a convenient way to avoid all those hard-coded indexes. And by naming the parameter variables properly, you can use object literal shorthand to create the resulting objects more easily.
var oldobj = [{
"product": ["1009", "name", "price", "image", "description"]
},
{
"product": ["1004", "name2", "price2", "image2", "description2"]
}
];
var newobj = oldobj.map(({product: [id, name, price, url, description]}) =>
({id: Number(id), name, price: Number(price), url: `url${url}.jpg`, description})
);
console.log(newobj);
Try this
let newObject = _.map(oldObject, (item) => {
return {
id: item.product[0],
name: item.product[1],
price: item.product[2],
image: item.product[3],
description: item.product[4]
};
});
If you want to convert an array of poorly structured object to an array of well structured ones, you can use Array.prototype.map from VanillaJS:
const data = [
{"product": ["1009", "name", "120", "image", "description"]},
{"product": ["1004", "name2", "250", "image2", "description2"]},
{"product": ["1012", "name3", "85", "image3", "description3"]}
];
const products = data.map(({ product }) => {
const [id, name, price, image, description] = product;
return {
id: Number(id),
name,
price: Number(price),
image: `url${image}.jpg`,
description
};
});
console.log(products);