How to merge an object inside another object - javascript

I don't know if the question is right, but I want to do the following, I want the properties of "variable" to become part of the variable day.
The object being as follows: {seg: 60, min:1, dias: 7, semana: 1}
Remaining the object of the following way: Take into consideration that the object inside the object could be anyone and it does not have to be the property "variable" for what I would like to make it dynamic, reason why an arrangement like delete dia.variable and later to merge would not serve me.
With the code that I show you I have only obtained that the result is: {seg: 60, min:1, variable:{dias: 7, semana: 1}, dias: 7, semana: 1}
Any ideas on how I could do it more efficiently, and above all, do it.
const dia = {seg: 60, min:1, variable:{dias: 7, semana: 1}}
let varia = {}
let newDia = {}
const processJson = () => {
Object.entries(dia).map(([name, value]) => {
if(typeof(value) === 'object'){
varia = value
newDia = {...dia, ...variable}
}
})
}
processJson()

This is a universal method for flatting any object with 2 levels deep. It uses the array reduce method and the Object.keys() function:
const dia = { seg: 60, min: 1, variable: {dias: 7, semana: 1} }
function flatObject(o) {
return Object.keys(o).reduce((result, key) => {
// If there is a nested object, flat the object
if (typeof o[key] === 'object' && ! Array.isArray(o[key]) && o[key] !== null) {
for (const inKey in o[key]) {
result[inKey] = o[key][inKey];
}
}
// Just copy the value
else {
result[key] = o[key];
}
// Return accumulator
return result;
}, {});
}
console.log(flatObject(dia))

Start of by using a destructuring assignement to extract the seg, min and variable properties from the dia object.
const { seg, min, variable } = dia;
Then create a new object in which you set the seg and min properties with the destructured properties. For the variable property, use the spread syntax to place the properties of variable inside the same object.
const result = {
seg,
min,
...variable
};
const dia = {
seg: 60,
min: 1,
variable: {
dias: 7,
semana: 1
}
};
const { seg, min, variable } = dia;
const result = {
seg,
min,
...variable
};
console.log(result);

Related

How to check if an object has other than specific properties

I have an object obj which has n number of possible properties
lets say some of them are known,
const someKnownProps = ["props.abc", "xyz"]; // or more
I want to know if obj has other than known properties in it.
To clarify:
obj can look like this:
obj = {
props: {
abc: {
def: 1
},
ghi: {
jkl: 2
}
},
xyz: 3
}
Doing Object.keys only return first level children,
in this case it will return props not props.abc
You can use Object.keys to get all keys and filter the keys which aren't included in the someKnownProps array.
const obj = {
"props.abc": 1,
"xyz": 2,
"three": 3,
"four": 4,
}
const someKnownProps = ["props.abc", "xyz"]; // or more
const unknownKeys = Object.keys(obj).filter(key => !someKnownProps.includes(key))
console.log(unknownKeys)
There are two (unrelated) tasks involved in this question:
Traversal of an object's properties
Comparison of a set of traversed object properties to a list of strings representing dot-notation-formatted object property accessors
While I'm sure the former has been previously discussed on SO, I'll provide an implementation of such an algorithm below in order to address the details of this question.
This is essentially a specific case of recursion where each cycle starts with these inputs:
an object
a dot-notation-formatted path
a Set of existing such paths
The code below includes inline comments explaining what's happening, and there are some console.log statements at the end to help you visualize some example results based on the data in your question. If something is unclear after reviewing the code, feel free to leave a comment.
'use strict';
/** #returns whether value is a non-null, non-array object */
function isObject (value) {
return value !== null && typeof value === 'object' && !Array.isArray(value);
}
/** #returns the enumerable (optionally including inherited) keys of an object */
function getKeys (obj, includeInherited = false) {
if (!includeInherited) return Object.keys(obj);
const keys = new Set();
let o = obj;
while (o !== null) {
for (const key of Object.keys(o)) keys.add(key);
o = Object.getPrototypeOf(o);
}
return [...keys];
}
/**
* #returns an array of strings representing all traversible branches
* of child objects, each formatted as a combined path of dot-notation
* property accessors
*/
function findObjectPaths (
obj,
{
includeInherited = false,
currentPath = '',
paths = new Set(),
skipReturn = false,
} = {},
) {
for (const key of getKeys(obj, includeInherited)) {
// Append the current dot-notation property accessor
// to the existing path of this object:
const path = `${currentPath}.${key}`;
// Add it to the set:
paths.add(path);
const o = obj[key];
// Recurse if the child value is an object:
if (isObject(o)) {
findObjectPaths(o, {
includeInherited,
currentPath: path,
paths,
skipReturn: true,
});
}
}
// If this is not a sub-cycle (it's the top-level invocation), then convert
// the set to an array and remove the first "." from each string
if (!skipReturn) return [...paths].map(p => p.slice(1));
}
// Use:
const obj = {
props: {
abc: {
def: 1,
},
ghi: {
jkl: 2,
},
},
xyz: 3,
};
let someKnownProps = ['props.abc', 'xyz'];
let objectPaths = findObjectPaths(obj);
let hasOtherProps = objectPaths.some(path => !someKnownProps.includes(path));
console.log(hasOtherProps); // true
// An example of all of the paths in the object above:
someKnownProps = [
'props',
'props.abc',
'props.abc.def',
'props.ghi',
'props.ghi.jkl',
'xyz',
];
objectPaths = findObjectPaths(obj);
hasOtherProps = objectPaths.some(path => !someKnownProps.includes(path));
console.log(hasOtherProps); // false
// Finally, comparing the results of inherited vs non-inherited enumeration:
const objWithoutOwnProps = Object.create({
props: {
abc: {
def: 1,
},
ghi: {
jkl: 2,
},
},
xyz: 3,
});
console.log(
'Non-inherited props:',
findObjectPaths(objWithoutOwnProps),
);
console.log(
'Inherited props:',
findObjectPaths(objWithoutOwnProps, {includeInherited: true}),
);
Similar to what Mina said:
let obj = {one: 1, two: 2, three: 3};
let knownKeys = ['one', 'two'];
for (let key in obj) {
if (!knownKeys.includes(key)) {
console.log(key);
}
}

Change all properties of an object

Is there a way to change all properties of an object to a given value, for example to 0? Will it work with nested objects?
I guess it's a noob question, but i started messing around with js only few days ago.
EDIT:
basically it looks like this:
var obj1 = {
obj11: {
a11: 1
b11: 2
c11: 321
},
obj12: {
a12: 31
b12: 65
c12: 8776
}
}
All those values are affected by some other functions. I wanted to make a "reset" function that would set all those values to 0. My expected output is:
{
obj11: {
a11: 0
b11: 0
c11: 0
},
obj12: {
a12: 0
b12: 0
c12: 0
}
}
let obj1 = {
obj11: {
a11:1,
b11:2,
c11:321,
},
obj12: {
a12:31,
b12:65,
c12:8776,
},
};
// Declare a function with a single argument o.
function reset(o) {
// Loop through all of the properties of the argument object o
for([key, value] of Object.entries(o)) {
const t = typeof value;
switch(t) {
// If the current property has an object value, handle that recursively.
case 'object': reset(value); break;
// If the current property has a numeric value, set the value to 0.
case 'number': o[key] = 0; break;
// Otherwise print some status information.
default: console.log('The property '+key+'is of an unhandled type ('+t+').');
}
}
}
reset(obj1);
// Print the result for convenience.
console.log(JSON.stringify({obj1}));
One way to do this is to take advantage of JSON.stringify(). Using its replacer argument, you can specify how each value should be transformed. This method will also recursively traverse your object, allowing you to change all values. As this function converts your object to a string, you'll need to convert it back using JSON.parse():
const obj1 = { obj11: { a11: 1, b11: 2, c11: 321 }, obj12: { a12: 31, b12: 65, c12: 8776 } };
const res = JSON.parse(JSON.stringify(obj1, (key, value) => Object(value) === value ? value : 0));
console.log(res);
The above uses an arrow function, which gets passed in a key and a value. This will be called for every key/value in your object. The value that you return from the arrow function is the new resulting value in the new object. In this case, that value is the is 0 when the value isn't an object, otherwise, if it is an object, we just return the original object.
Note: If you have an object type that isn't serializable (such as a function) as one of your values, then this method will remove that key-value pair.
If your object has a consistent structure and doesn't contain infinitely nested objects, you might find it more straightforward to loop over the keys of your obj1 (obj11 and obj12) using a for..in loop, and then the keys of the inner objects which you can obtain by using the currently looped on key from your outer loop:
const obj1 = { obj11: { a11: 1, b11: 2, c11: 321 }, obj12: { a12: 31, b12: 65, c12: 8776 } };
const res = {};
for(const key in obj1) {
res[key] = {};
for(const innerKey in obj1[key]) {
res[key][innerKey] = 0;
}
}
console.log(res);
Finally, you could use a recursive approach, where you use a function to loop the entries (a [[key, value], ...] pair array) of your object, with .map() to convert every value to a 0. You can then use Object.fromEntries() to build your object back together from the entries array. If you encounter an object as one of your values, you can recall this function to apply the same logic to the nested value:
const obj1 = { obj11: { a11: 1, b11: 2, c11: 321 }, obj12: { a12: 31, b12: 65, c12: 8776 } };
const changeVals = (obj, newVal) =>
Object.fromEntries(Object.entries(obj).map(
([key, val]) =>
!Array.isArray(val) && Object(val) === val && typeof val === "object"
? [key, changeVals(val, newVal)]
: [key, newVal]
)
);
console.log(changeVals(obj1, 0));

How to modify each value of a nested object?

How can I multiply every nested value of this object by X (e.g. 0.5)?
const myObject = {
base: {
serving: {
size: 100
}
},
fat: {
acids: {
monoUnsaturatedFattyAcids: 12
polyUnsaturatedFattyAcids: 3
saturatedFattyAcids: 2
},
}
}
The object is sometimes nested up to 10 levels deep.
You could define a generator that will provide an iterator over every nested key/value pair (together with the nested object), so that you can do what you want with it inside a for loop:
function * iter(obj) {
for (let [key, value] of Object.entries(obj)) {
if (Object(value) !== value) yield [obj, key, value];
else yield * iter(value);
}
}
// demo
const myObject = {
base: {
serving: {
size: 100
}
},
fat: {
acids: {
monoUnsaturatedFattyAcids: 12,
polyUnsaturatedFattyAcids: 3,
saturatedFattyAcids: 2
},
}
};
for (let [obj, key, value] of iter(myObject)) {
if (typeof value === "number") obj[key] *= 0.5; // multiply by 0.5
}
// The object has been mutated accordingly
console.log(myObject);
function modifyValues(obj) {
for (let key in obj) {
if (typeof obj[key] === "object") {
modifyValues(obj[key]);
}else {
obj[key] = obj[key] * 0.5;
}
}
return obj;
}
console.log(modifyValues({
"a": 5,
"b": {
"c": 10
},
"d": {
"e": 15,
"f": {
"g": 20
}
}
}))
As suggested here's an explanation:
You can try something like a function that recursively iterates through the properties of an object and depending on said property its type, multiply or call our recursive function again.
These are the steps the function takes:
We use the Object.keys method to get an array containing all property names as strings of our object
We iterate through our keys array
for every key we check if obj[key] its value is either a number or something else.
note about obj[key]: by using square braces you can access properties of an object by passing a string. e.g obj['base'] is equivalent to obj.base
if it's type is indeed number, multiply obj[key] by 0.5! Don't forget to actually assign the value to the property.
if it ain't a number, call the function again, but this time we use the object stored in obj[key].
e.g. when you pass myObject to the function, the first key will be base, obj.base contains an object, so we call recursiveMultiplication(obj.base) and the cycle continues.
until every recursiveMultiplication call runs out of keys to iterate through.
When all is said and done, the original object should contain mutated values.
If you don't wish to mutate you should clone the object using something like rfdc. using {...spread} won't cut it for nested objects.
const myObject = {
base: {
serving: {
size: 100
}
},
fat: {
acids: {
monoUnsaturatedFattyAcids: 12,
polyUnsaturatedFattyAcids: 3,
saturatedFattyAcids: 2
}
}
};
const recursiveMultiplication = (obj) => {
Object.keys(obj).forEach(key => typeof obj[key] === "number" ? obj[key] = obj[key] * 0.5 : recursiveMultiplication(obj[key]))
return obj;
};
console.log(recursiveMultiplication(myObject));
I think the best is to use a library to ease the traverse of the nested object.
const _ = require('lodash')
const myObject = {
base: {
serving: {
size: 100
}
},
fat: {
acids: {
monoUnsaturatedFattyAcids: 12,
polyUnsaturatedFattyAcids: 3,
saturatedFattyAcids: 2
},
}
}
const newObjValue = _.cloneDeepWith(myObject, x => typeof x === 'number'? x*0.5: undefined)
console.log(newObjValue)
Here is quick solution. I have not added cases for arrays. need to add them if you expect arrays at the top level or nested inside.
const myObject = {
base: {
serving: {
size: 100
}
},
fat: {
acids: {
monoUnsaturatedFattyAcids: 12,
polyUnsaturatedFattyAcids: 3,
saturatedFattyAcids: 2
},
}
}
function isArray ( obj ) {
return isObject(obj) && (obj instanceof Array);
}
function isObject ( obj ) {
return obj && (typeof obj === "object");
}
function recursiveMultiplyNumberFields(myObject, X){
//Assuming that at the top level, what you pass on is a dictionar object. If it could be arrays as well, need to handle more cases
for (key in myObject){
if (isNaN(myObject [key])==false){
myObject[key] = X*myObject[key];
}
else if(isArray(myObject [key])){
/*not taken care as of now. Need to do if ararys are expected inside*/
}
else if(typeof myObject [key] === 'object' && myObject [key] !== null){
recursiveMultiplyNumberFields(myObject [key],X)
}
else{
//not a number and not a object. So need not do anything
}
}
}
console.log("Before mult",myObject);
recursiveMultiplyNumberFields(myObject,5);
console.log("After mult",myObject);

How to create function where you pass object and key and get back value of nested object [duplicate]

This question already has answers here:
Access Javascript nested objects safely
(14 answers)
Closed 3 years ago.
I have a nested object, how do I have function that you pass in the object and a key and get back the value?
Example Inputs
object = {"a1":{"b1":"{"c1":"d1"}"}}
key = a1/b1/c1
object = {"x1":{"y1":"{"z1":"a1"}"}}
key = x1/y1/z1
value = a1
Below is what I have attempted but it's wrong
var obj, traverse;
obj = {
a1: {
b1: c1,
b1: d1
},
x1: {
y1: z1,
y1: a1
}
};
traverse = function(node, path) {
var pairs;
if (!(pairs = _(node).pairs()).length) {
return [
{
keys: path,
value: node
}
];
} else {
return [].concat.apply([], _(pairs).map(function(kv) {
return traverse(kv[1], path.concat(kv[0]));
}));
}
};
console.log(traverse(obj, []));
If you can express your keys as an array you can solve this with a reduce:
const obj = {"x1":{"y1":{"z1":"a1"}}}
const keys = ['x1', 'y1', 'z1']
const value = keys.reduce((acc,key)=>acc[key], obj)// "a1"
As a function that accepts an array of keys or a string of the form 'x1.y1.z1', with a fallback for undefined values:
const getValueFromKeys = (obj, keys, defaultValue)=> (Array.isArray(keys)?keys:keys.split('.')).reduce((acc,key)=>acc[key] || defaultValue, obj)
You could try lodash _.get , it's brilliant for this type of access:
What's nice too is you can pass in a default value if the path is not found.
var object = {"a1": { "b1": { "c1": "d1" }}};
console.log("Result at 'a1.b1.c1': ",_.get(object, 'a1.b1.c1'));
console.log("Result at 'a1.b1.nonexistent key': ",_.get(object, 'a1.b1.nonexistent', "default result"));
var object2 = {"x1":{"y1":{"z1":"a1"}}};
console.log("Result at 'x1.y1.z1': ",_.get(object2, 'x1.y1.z1'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js"></script>
let obj = {
a1: {
b1: {
nested: true
},
},
x1: {
y1: 'zl',
}
};
function getObjectKeys(object, key) {
// if you want another split key you can change this.
const keys = key.split('.');
let obj = object;
for (let ikey of keys) {
for (let [objKey, value] of Object.entries(obj)) {
if(!keys.includes(objKey)) {
continue;
}
obj = value;
}
}
return obj;
}
console.log(getObjectKeys(obj, 'a1.b1.nested'));
console.log(getObjectKeys(obj, 'x1.y1'));
// even if there is another additional key
console.log(getObjectKeys(obj, 'x1.y1.nested'));
Without using any library,
read more about Object.entries

Multiple key names, same pair value

I'm trying to setup an object literal in a JavaScript script that has a key with multiple names. referring to the same object value i.e. something like these that I have already tried:
var holidays: {
"thanksgiving day", "thanksgiving", "t-day": {
someValue : "foo"
}
}
var holidays: {
["thanksgiving day", "thanksgiving", "t-day"]: {
someValue : "foo"
}
}
Is there a way I can accomplish this?
Another approach is to do some postprocessing
function expand(obj) {
var keys = Object.keys(obj);
for (var i = 0; i < keys.length; ++i) {
var key = keys[i],
subkeys = key.split(/,\s?/),
target = obj[key];
delete obj[key];
subkeys.forEach(function(key) { obj[key] = target; })
}
return obj;
}
var holidays = expand({
"thanksgiving day, thanksgiving, t-day": {
someValue : "foo"
}
});
JSON does not offer such a feature, nor do Javascript object literals.
You might be able to make do with something like this:
holidays = {
thanksgiving: {foo: 'foo'},
groundhogDay: {foo: 'bar'},
aliases: {
'thanksgiving day': 'thanksgiving',
't-day': 'thanksgiving',
'Bill Murrays nightmare': 'groundhogDay'
}
}
and then you can check
holidays[name] || holidays[holidays.aliases[name]]
for your data.
It's not a wonderful solution. But it wouldn't be too difficult to write a little function that created this sort of object out of a representation like:
[
{
names: ['thanksgiving', 'thanksgiving day', 't-day'],
obj: {foo: 'foo'}
},
{
names: ['groundhogDay', 'Bill Murrays nightmare'],
obj: {foo: 'bar'}
},
]
if that would be easier to maintain.
Another solution, if you can afford RegExp execution, and ES6 Proxy:
let align = new Proxy({
'start|top|left': -1,
'middle|center': 0,
'end|bottom|right': 1,
}, {
get: function(target, property, receiver) {
for (let k in target)
if (new RegExp(k).test(property))
return target[k]
return null
}
})
align.start // -1
align.top // -1
align.left // -1
align.middle // 0
align.center // 0
align.end // 1
align.bottom // 1
align.right // 1
See MDN Proxy
2021 EDIT:
Another (cleaner?) solution using reduce & defineProperty :
const myDict = [
// list of pairs [value, keys],
// note that a key should appear only once
[-1, ['start', 'left', 'top']],
[0, ['center', 'middle']],
[1, ['end', 'right', 'bottom']],
].reduce((obj, [value, keys]) => {
for (const key of keys) {
Object.defineProperty(obj, key, { value })
}
return obj
}, {})
I guess you could do something like this:
var holidays = {
'thanksgiving day': {
foo: 'foo'
}
};
holidays.thanksgiving = holidays['t-day'] = holidays['thanksgiving day'];
If you see yourself doing this often or you have more values consider this pattern:
'thanksgiving, t-day, thanks, thank, thank u'.split(',').forEach(function(key) {
holidays[key] = holidays['thanksgiving day'];
});
A better approach would be to process your data beforehand instead of adding duplicates.
That should work as expected:
function getItem(_key) {
items = [{
item: 'a',
keys: ['xyz','foo']
},{
item: 'b',
keys: ['xwt','bar']
}];
_filtered = items.filter(function(item) {
return item.keys.indexOf(_key) != -1
}).map(function(item) {
return item.item;
});
return !!_filtered.length ? _filtered[0] : false;
}
With ES6 you could do it like this, but it's not ideal:
const holidays = {
"single": {
singleValue: "foo",
},
...([
"thanksgiving day", "thanksgiving", "t-day",
].reduce((a, v) => ({...a, [v]: {
someValue: "foo",
}}), {})),
"other": {
otherValue: "foo",
},
};
I still think the cleanest solution is probably:
let holidays = {
"t-day": {
someValue: "foo",
},
};
holidays["thanksgiving"] = holidays["t-day"];
holidays["thanksgiving day"] = holidays["t-day"];
Now this may be overkill for you, but here's a generic function that will create an object with "multiple keys." What it actually does is have one real property with the actual value, and then defines getters and setters to forward operations from the virtual keys to the actual property.
function multiKey(keyGroups) {
let obj = {};
let props = {};
for (let keyGroup of keyGroups) {
let masterKey = keyGroup[0];
let prop = {
configurable: true,
enumerable: false,
get() {
return obj[masterKey];
},
set(value) {
obj[masterKey] = value;
}
};
obj[masterKey] = undefined;
for (let i = 1; i < keyGroup.length; ++i) {
if (keyGroup.hasOwnProperty(i)) {
props[keyGroup[i]] = prop;
}
}
}
return Object.defineProperties(obj, props);
}
This is less sketchy than you would expect, has basically no performance penalty once the object is created, and behaves nicely with enumeration (for...in loops) and membership testing (in operator). Here's some example usage:
let test = multiKey([
['north', 'up'],
['south', 'down'],
['east', 'left'],
['west', 'right']
]);
test.north = 42;
test.down = 123;
test.up; // returns 42
test.south; // returns 123
let count = 0;
for (let key in test) {
count += 1;
}
count === 4; // true; only unique (un-linked) properties are looped over
Taken from my Gist, which you may fork.
Same reponse (ES6 Proxy, RegExp), but in a shorter way (and significantly less legible)
let align = new Proxy({
'start|top|left': -1,
'middle|center': 0,
'end|bottom|right': 1,
}, { get: (t, p) => Object.keys(t).reduce((r, v) => r !== undefined ? r : (new RegExp(v).test(p) ? t[v] : undefined), undefined) })
align.start // -1
align.top // -1
align.left // -1
align.middle // 0
align.center // 0
align.end // 1
align.bottom // 1
align.right // 1
//create some objects(!) you want to have aliases for..like tags
var {learn,image,programming} =
["learn", "image", "programming"].map(tag=>({toString:()=>tag }));
//create arbitrary many aliases using a Map
var alias = new Map();
alias.set("photo", image);
alias.set("pic", image);
alias.set("learning", learn);
alias.set("coding", programming);
//best put the original tagNames in here too..
//pretty easy huh?
// returns the image object
alias.get("pic");
// ;)
here is a way you can initialize an object with several keys sharing the same value
var holidays = {
...["thanksgiving day", "thanksgiving", "t-day"].reduce((acc, key) => ({ ...acc, [key]: 'foo' }), {})
}
although I would personally think it was more clear if it was written out
Object.fromEntries produces some fairly readable and concise code:
var holidays = Object.fromEntries(
["thanksgiving day", "thanksgiving", "t-day"].map(k => [k, "foo"]));
The spread syntax can be used to include this alongside other key/value pairs:
var holidaysAndMore = {
"A": "a",
...Object.fromEntries(
["thanksgiving day", "thanksgiving", "t-day"].map(k => [k, "foo"])),
"B": "b"
};

Categories

Resources