Create objects from main key and value from nested object - javascript

I'll keep it simple. Suppose I have an object like this:
let myObj = {
name:{
value: "John",
type: "contains"
},
age:{
value: "5",
type: "contains"
}
}
how can I create a new object that contains the main key but the value is just the value of its nested object, as follows:
let myNewObj = {
name: "John",
age: "5"
}
Thanks in advance.

If you just want to extract the value key for each object, you can do something like this:
let myObj = {
name:{
value: "John",
type: "contains"
},
age:{
value: "5",
type: "contains"
}
}
let newObj = {}
for (const key in myObj) {
newObj[key] = myObj[key].value;
}
console.log(newObj);
// {
// age: "5",
// name: "John"
// }

Convert the object to its entries array and map through it to return [key,value]
Then convert to a new object using Object.fromEntries
let myObj = {
name:{
value: "John",
type: "contains"
},
age:{
value: "5",
type: "contains"
}
}
let myNewObj = Object.fromEntries(Object.entries(myObj).map(([key,{value}])=>[key,value]))
console.log(myNewObj)

In general, to be able to transform all the values of an object's properties according to a mapping function, you can use Object.entries to make an array of [key, value] arrays for each property, and then use Object.fromEntries to reconstitute an object from those arrays. You can provide a generic transformation function in the middle of that operation like this:
const transformValues = (obj, transform) =>
Object.fromEntries(Object.entries(obj).map(([key, value]) => [key, transform(value)]));
The transform function you need in your specific case will take the property value (which is an object) and just return its value property. Like this: ({ value }) => value (This is pretty easy with destructuring.)
let myObj = {
name: {
value: "John",
type: "contains"
},
age: {
value: "5",
type: "contains"
}
}
const transformValues = (obj, transform) =>
Object.fromEntries(Object.entries(obj).map(([key, value]) => [key, transform(value)]));
const result = transformValues(myObj, ({ value }) => value);
console.log(result);

Related

Unstring an object property name javascript

I have an object similar to this:
const obj = {
id: 1,
name: {
"english-us": "John",
"english-uk": "John",
"italian-eu": "Giovanni",
},
};
I want to transfrorm every property name that is a string into a non-string one, like this:
const obj = {
id: 1,
name: {
english_us: "John",
english_uk: "John",
italian_eu: "Giovanni",
},
};
I can't modify the original object. I get it from an axios request.
You could use regex with stringify
let output = JSON.parse(JSON.stringify(obj).replace(/"(.*?)":.*?,?/g,
key=>key.replace(/\-/g, `_`)));
Output
console.log(JSON.stringify(output, null, 4));
/*
{
"id": 1,
"name": {
"english_us": "John",
"english_uk": "John",
"italian_eu": "Giovanni"
}
}*/
If you can copy the object, you could check this solution for declaring the attributes:
link
There are a few ways of achieving this. This example has a function that converts the key on every iteration of the name entries. A new names object is updated with these properties, and is later folded into a new object along with the existing properties of the original object.
const obj = {
id: 1,
name: {
"english-us": "John",
"english-uk": "John",
"italian-eu": "Giovanni",
},
};
const convert = (key) => key.replace('-', '_');
const updatedName = {};
for (const [key, value] of Object.entries(obj.name)) {
updatedName[convert(key)] = value;
}
const newObj = { ...obj, name: updatedName };
console.log(newObj);
You can convert object to JSON and convert back.
const obj = {
id: 1,
name: {
"english-us": "John",
"english-uk": "John",
"italian-eu": "Giovanni",
},
};
console.log(JSON.parse(JSON.stringify(obj)))
Two ways to clone the object and rename all keys from its name property
const obj = {
id: 1,
name: {
"english-us": "John",
"english-uk": "John",
"italian-eu": "Giovanni",
},
};
// clone obj
const myObj = window.structuredClone ?
structuredClone(obj) : JSON.parse(JSON.stringify(obj));
// rename all keys in myObj.name
Object.keys(myObj.name).forEach(key => {
myObj.name[key.replace(/\-/g, `_`)] = myObj.name[key];
delete myObj.name[key];
});
console.log(myObj.name.english_us);
// obj is untouched
console.log(obj.name[`english-us`]);
// myObj.name[`english-us`] does not exist
console.log(myObj.name[`english-us`]);
// alternative: clone and rename in one go
const myObjClone = {
...obj,
name: Object.fromEntries(
Object.entries(obj.name)
.reduce( (acc, [k, v]) =>
[ ...acc, [ k.replace(/\-/g, `_`), v ] ] , [] ) )
};
console.log(myObjClone.name.italian_eu);
// obj is untouched
console.log(obj.name[`italian-eu`]);
// myObjClone.name[`italian-eu`] does not exist
console.log(myObjClone.name[`italian-eu`]);

how do i sum up a value from an array depending on the date and id

i have the following array instance
this.array=[{id:"121",score:"5",createdOn:"2022-05-17T19:52:23.6846702+00:00"}
{id:"121",score:"8",createdOn:"2022-05-19T13:00:00.6846702+00:00"}
{id:"122",score:"7",createdOn:"2022-04-11T08:00:00.6846702+00:00"}
{id:"121",score:"1",createdOn:"2022-03-12T12:00:00.6846702+00:00"}
]
how do i some the values with a matching id and month.
for example my ouput should be
newArray=[{id:"121",score:13,month:"May"}
{id:"122",score:7,month:"April"}
{id:"121",score:1,month:"March"}
]
i tried something like this
this.array.forEach(item => {
var obj = {}
if (obj.hasOwnProperty(item.id)) {
obj["id"] = obj[item.id] + parseFloat(item.score);
}
else {
obj["id"] = parseFloat(item.score);
}
this.newArray.push(obj);
});
But it didnt work and i dont know how to check the month
You can use a fairly standard 'group by' using a compound key of id_month.
The example uses reduce() to iterate the array, toLocaleDateString() to retrieve the month name from the ISO date string and a template literal to create the compound key.
The score should be converted to a number before adding to avoid accidental concatenation, here using the unary plus (+) operator.
Finally we take just the Object.values of the grouped object as the result.
const array = [{ id: "121", score: "5", createdOn: "2022-05-17T19:52:23.6846702+00:00" }, { id: "121", score: "8", createdOn: "2022-05-19T13:00:00.6846702+00:00" }, { id: "122", score: "7", createdOn: "2022-04-11T08:00:00.6846702+00:00" }, { id: "121", score: "1", createdOn: "2022-03-12T12:00:00.6846702+00:00" },];
const result = Object.values(
array.reduce((a, { id, createdOn, score, ...rest }) => {
const month = new Date(createdOn).toLocaleDateString('en', { month: 'long' });
a[`${id}_${month}`] ??= { id, month, score: 0, ...rest };
a[`${id}_${month}`].score += +score;
return a;
}, {})
)
console.log(result)
The exact same logic can be used in a standard for...of loop instead of within a reduce() call if you prefer.
const array = [{ id: "121", score: "5", createdOn: "2022-05-17T19:52:23.6846702+00:00" }, { id: "121", score: "8", createdOn: "2022-05-19T13:00:00.6846702+00:00" }, { id: "122", score: "7", createdOn: "2022-04-11T08:00:00.6846702+00:00" }, { id: "121", score: "1", createdOn: "2022-03-12T12:00:00.6846702+00:00" },];
const grouped = {}
for (const { id, createdOn, score, ...rest } of array) {
const month = new Date(createdOn).toLocaleDateString('en', { month: 'long' });
grouped[`${id}_${month}`] ??= { id, month, score: 0, ...rest }
grouped[`${id}_${month}`].score += +score;
}
const result = Object.values(grouped);
console.log(result)
Use a temporary object to hold the updated information.
Because you need to separate out the objects that have the same ids but that were created on different months you can use a key id-month on the temporary object to identify them.
Loop over the array of objects getting the month name, and create the key. (I've used Intl.DateTimeFormat here because you can pass in a language string to get a different result from the function - try 'es-ES' for example.)
If the property with that key doesn't exist on the temporary object add a default object to the temporary object with a default object value, and then increase its score (making sure you coerce it to a number first).
Finally get the Object.values which will return an array of all those values in the temporary object.
const arr=[{id:"121",score:"5",createdOn:"2022-05-17T19:52:23.6846702+00:00"},{id:"121",score:"8",createdOn:"2022-05-19T13:00:00.6846702+00:00"},{id:"122",score:"7",createdOn:"2022-04-11T08:00:00.6846702+00:00"},{id:"121",score:"1",createdOn:"2022-03-12T12:00:00.6846702+00:00"}];
const temp = {};
const language = 'en-GB';
function getMonth(createdOn, language) {
const date = new Date(createdOn);
return new Intl.DateTimeFormat(language, { month: 'long' }).format(date);
}
for (const obj of arr) {
const { id, score, createdOn } = obj;
const month = getMonth(createdOn, language);
const key = `${id}-${month}`;
temp[key] ??= { id, score: 0, month };
temp[key].score += Number(score);
}
console.log(Object.values(temp));
Additional documentation
Destructuring assignment
for/of

Merging two Javascript objects with similar keys

I need to merge two objects(obj1, obj2), that happen to share similar keys.
obj1 = {
0:{"Example1": "Example1"},
1:{"Example1": "Example1"},
2:{"Example1": "Example1"}
}
obj2 = {
0:{"Example2": "Example2"},
1:{"Example2": "Example2"},
2:{"Example2": "Example2"}
}
Expected result:
obj3 = {
0:{"Example1": "Example1"},
1:{"Example1": "Example1"},
2:{"Example1": "Example1"},
3:{"Example2": "Example2"},
4:{"Example2": "Example2"},
5:{"Example2": "Example2"},
}
Usual approach when merging two objects:
const obj3 = Object.assign({}, obj1, obj2);
Problem: They do share many keys, as such, in obj3, only the contents of obj2 are going to be found.
My approach:
let obj3= Object.assign({}, obj1);
for(let i=0; i<obj2.length; i++) {
obj3[obj3.length + i] = obj2[i];
}
Question: Is there, another more elegant, pre-defined way of merging two objects with similar keys?
Although I still think obj1 and obj2 should be arrays...
const obj1 = {
0:{"Example1": "Example1"},
1:{"Example1": "Example1"},
2:{"Example1": "Example1"}
};
const obj2 = {
0:{"Example2": "Example2"},
1:{"Example2": "Example2"},
2:{"Example2": "Example2"}
}
const result = Object.fromEntries(
Object.values(obj1) // get the values of the first object
.concat(Object.values(obj2)) // get the values of the second object and add them to the values of the first
.map((value, index) => [ index, value ]) // re-index them
// or if you need actual copies of the "inner" objects
/*
.map((value, index) => [
index,
Object.assign({}, value)
])
*/
);
console.log(result);
Is this "more elegant". Maybe...
The objects in your code are key-value pairs rather than a simple list (array).
From the looks of it there are only two possible scenarios:
Your objects have a meaningful, unique key associated to them (for example, this very question on stackoverflow has key 69316153 – look at the URL bar). In this case, you really can't merge the two as they have conflicting keys. Think if there was another question on this website with the same ID as this one!
The keys are not meaningful and you're happy with the same object being re-assigned a different key. In this case the correct data structure to use is arrays (obj1 = [{"Example": "..."}, {"Example": "..."}]).
If the latter is your situation, this code will work:
const obj3 = Object.values(obj1).concat(Object.values(obj2))
Object.values(obj) returns an array of values, discarding all of the keys.
Let's say we have:
const obj1 = {
1: { Name: "Apple" },
2: { Name: "Watermelon" },
};
const obj2 = {
2: { Name: "Pear" },
5: { Name: "Tomato" }
};
Object.values(obj1) will return [{ Name: "Apple" }, { Name: "Watermelon" }], while Object.values(obj2) will return [{ Name: "Pear" }, { Name: "Tomato" }].
With const obj3 = Object.values(obj1).concat(Object.values(obj2)) you will end up with:
obj3 = [
{ Name: "Apple" },
{ Name: "Watermelon" },
{ Name: "Pear" },
{ Name: "Tomato" }
];
Because you have key-value maps and not arrays, you can't just combine the objects. Your only way would be to iterate through all the keys and add them to the final result.
E.g. something along the lines:
const obj1 = {
0:{"Example1": "Example1"},
1:{"Example1": "Example1"},
2:{"Example1": "Example1"}
};
const obj2 = {
0:{"Example2": "Example2"},
1:{"Example2": "Example2"},
2:{"Example2": "Example2"}
};
function merge(...args) {
return Object.fromEntries( // Create entries
args // From all arguments
.flatMap(o => Object.values(o)) // Get values (discard keys)
.map((element, index) => [ index, element ]) // Remap them
);
}
console.log(merge(obj1, obj2));
// Will output
// {
// 0: { Example1: "Example1" },
// 1: { Example1: "Example1" },
// 2: { Example1: "Example1" },
// 3: { Example2: "Example2" },
// 4: { Example2: "Example2" },
// 5: { Example2: "Example2" }
// }

How to do: All contents into one object in this case

I'm trying to modify object's key to given name + its key name and want to get all into one object.
Source:
aodl = {"name1": "Jhon", "name2": "Tiger", "name3": "Jack", ...}
When I tried
console.log(
Object.entries(aodl).map(([key, value]: [string, string]) => {
return { ["aodl_" + key]: value };
})
);
Then I get
[ {"aodl_name1": "Jhon"}, {"aodl_name2": "Tiger"}, {"aodl_name3": "Jack"}, {…}, {…}, ...]
I want to get all object's contents and I want to put them into one object
{
"aodl_name1": "Jhon",
"aodl_name2": "Tiger",
"aodl_name3": "Jack",
...
}
How to do it?
Please let me know if you need more info about it.
You could either use Object.fromEntries
var aodl = { foo: 'foo', bar: 'bar' },
object = Object.fromEntries(Object
.entries(aodl)
.map(([key, value]) => ["aodl_" + key, value])
);
console.log(object);
Or map single objects and get a single object with Object.assign.
var aodl = { foo: 'foo', bar: 'bar' },
object = Object.assign(
{},
...Object.entries(aodl).map(([key, value]) => ({ ["aodl_" + key]: value }))
);
console.log(object);
You could use simply Object.assign()
const aodl = { name: 'John', name1: 'Wick' }
console.log(Object.assign({}, ...Object.entries(aodl).map(([key, value]) => ({['aodl_'+key]: value}))))

How to map matching property values into same object?

I'm mapping matching Objects into one where the ID property values match.
So far the reduce and map works fine for the ID and Fet properties.
But now I want to map the Name property to each new mapped object. I did try the following but this creates a new Object separate to the others with the Name values.
How can I map matching property values to an object?
https://jsfiddle.net/brianVarley/zs8xadht/
This is what I tried in order to get the Name mapping:
const arr = [{ ID:12, Fet: "Donuts", Name: "DD" } , { ID: 12, Fet: "Cake", Name: "DD" }, { ID: 13, Fet: "Lemon", Name: "CC"}] ;
const grouped = arr.reduce((a,v) => {
if( !(v.ID in a) ) a[v.ID] = [];
a.Name = v.Name
a[v.ID].push(v.Fet);
return a;
},{});
var newArrayOfReducedObjects = Object.keys(grouped).map(key => {
return { ID: key, Name: key, Fet: grouped[key] };
});
console.log(newArrayOfReducedObjects);
I think you should use generic group by function which you can use with various keys for an array of objects.
var customGroupBy = function(xs, key) {
return xs.reduce(function(rv, x) {
(rv[x[key]] = rv[x[key]] || []).push(x);
return rv;
}, {});
};
Then you can call this function for any key:
customGroupBy (arr,"ID")
customGroupBy (arr,"Name")
The last call above will result in (JSONified)
"{"DD":[{"ID":12,"Fet":"Donuts","Name":"DD"},{"ID":12,"Fet":"Cake","Name":"DD"}],"CC":[{"ID":13,"Fet":"Lemon","Name":"CC"}]}"

Categories

Resources