var data = {
'id': 'object1',
'sceneCapability': {
'updatedAt': '2017-06-19T20:52:45.688Z'
'currentScene': {
'value': {
'number': 1,
'name': '1'
}
},
'outOfTune': {
'value': false
}
},
'lightingCapability': {
'intensity': {
'value': 0
}
},
'tiltCapability': {
'command': {
'value': 'NO'
},
'position': {
'value': 0
}
}
// like this I have different types of more than 20 Capabilities
};
How can I write a generic method to parse this Object? I need to get currentScene value, outOfTune, intensity, command, position, etc...
Sometimes I get only one capability and sometime I get more than 20 capabilities.
I want to avoid doing something like this because in future there might be hundreds of different capabilities
if (obj.lightingCapability && obj.lightingCapability.intensity) {
console.log(obj.lightingCapability.intensity.value)
}
if (device.sceneCapability && device.sceneCapability.outOfTune) {
// do something
}
Output I want something like
currentScene:1,
outOfTune: false,
intensity: 0,
command: 'NO',
position: 0
Maybe something like this will work for you?
A helper function that finds the property you need and returns null if anything along the chain doesn't exist. I added two 'different' versions in case you don't like the array of property names.
var object = {
a: {
b: {
c: {
d: 10
}
}
}
};
function getValue(object, propertyPath) {
var o = object;
var pLen = propertyPath.length;
for (var i = 0; i < pLen; i++) {
var propertyName = propertyPath[i];
if (!o.hasOwnProperty(propertyName))
return undefined;
o = o[propertyName];
}
return o;
}
function getValueFromString(object, path) {
return this.getValue(object, path.split('.'));
}
console.log(getValue(object, ['a', 'b', 'c', 'd'])); //logs 10
console.log(getValueFromString(object, 'a.b.c.d')); //logs 10
console.log(getValue(object, ['a', 'b', 'c', 'e'])); //logs undefined
Based on the discussion we had in the comments of my first answer I realized you meant something different. This should do the trick:
var object = {
a: {
b: {
c: {
value: 10
},
d: {
e: {
value: 20
}
}
}
}
};
function logAllValues(object) {
for (var p in object) {
var o = object[p];
if (o.value)
console.log(p + ': ' + o.value);
else
logAllValues(o);
}
}
logAllValues(object); //logs c:10 and e:20
A slightly hacky way to do this would be to create a helper function that allows the key chain to be passed in as a string and loop over it. For example
function getValue(obj, keyChain){
var keys = keyChain.split('.');
do {
var key = keys.shift();
if (!obj.hasOwnProperty(key)){
return undefined;
}
obj = obj[key];
} while (keys.length > 0);
return obj;
}
getValue(data, "lightingCapability.intensity.value")
I think you just need to install lodash#get
npm i --save lodash.get
var get = require('lodash.get');
if(get('foo.baz.foobaz')) {
alert('yep');
}
but you always will need to know all the paths you need in advance.
Re-implementing this well community tested method will end up in re-inventing the wheel, so, just install and use it.
you can implement some thing like this using ES6 try and catch block
var object = {
a: {
b: {
c: {
value: 10
},
d: {
e: {
value: 20
}
}
}
}
};
function getValue(jsObject) {
try {
return jsObject();
} catch (e) {
return undefined;
}
}
// use it like this
getValue(() => object.a.b); // returns Object {c: Object, d: Object}
getValue(() => object.a.b.c); // returns Object {value: 10}
getValue(() => object.a.b.x); // returns undefined
Related
I am using lodash to manipulate a JSON object. I am not against using Vanilla JS but since I am working on a PoC for now, I am just looking for the fastest solution to test.
So here is the problem I am facing: I want to be able to easily push an element to an array anywhere in an object, and it should create automatically all the missing nodes, including the last array.
For example, let's say I have an empty object, and I would like to create a function that can populate my object with the right values, for example:
let dl = {};
customPush(dl, 'a.b', { c: 3, d: 4 });
// or
customPush(dl, ['a', 'b'], { c: 3, d: 4 });
Should create:
dl = {
a: {
b: [{
c: 3,
d: 4
}]
}
}
This is everything I tried but none of them are working:
function customPush(obj, path, item) {
// This is just assigning the item to the path, not pushing to a new array
_.set(dl, path, item);
// This one is not doing anything visible
_.get(dl, path, []).push(item);
// Pushing in this one doesn't work with a path like 'a.b'
if (_.has(dl, path)) {
dl.path.push(item);
} else {
_.set(dl, path, [item]);
}
// Any idea?
...
}
Thank you very much for your help.
Your attempt here is very close:
// Pushing in this one doesn't work with a path like 'a.b'
if (_.has(dl, path)) {
dl.path.push(item);
} else {
_.set(dl, path, [item]);
}
You simply need to use _.get if the array is there and _.set if it isn't. You are already doing the latter part.
function customPush(obj, path, item) {
if (_.has(obj, path)) {
let arr = _.get(obj, path);
arr.push(item)
} else {
_.set(obj, path, [item]);
}
}
let objOne = { }
let objTwo = { a: [] }
let objThree = {
a: {
b: {
c: {
}
}
}
}
let objFour = {
a: {
b: {
c: {
d: []
}
}
}
}
customPush(objOne, "a", "item");
console.log("objOne", objOne);
customPush(objTwo, "a", "item");
console.log("objTwo", objTwo);
customPush(objThree, "a.b.c.d", "item");
console.log("objThree", objThree);
customPush(objFour, "a.b.c.d", "item");
console.log("objFour", objFour);
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.11/lodash.min.js"></script>
Worth noting that this only works if the key either doesn't exist or it it's value is an array. If you give the path to an existing key with a non-array value you'd get an error. You can check that using _.isArray but I am not sure what you want to do if a key exists and does not hold an array.
Consider a function returns an nested object and I want to modify the property inside the nested object.
In the below example, I'm calling the function many times or I need to store it in a temporary variable. Is there a way to invoke only once inside the braces and spread/modify inside the same object many times.
const getObject = () => {
return {
a: {
b: {
c: 1,
d: 2,
}
},
e: 3
}
}
var modifiedD = {
...getObject(),
a: {
b: {
...getObject().a.b,
d: 4
}
}
}
console.log(modifiedD);
when declaring a key after ...getObject() it replace the whole value. It does not merge the inner object behind a.
So you could do it as you have done and call getObject() multiple time.
An other solution could be to handle it using a function of your own merging the objects, like :
function mergeObjects(obj1, obj2) {
// We are going to copy the value of each obj2 key into obj1
Object.keys(obj2).forEach((x) => {
// If we have an object, we go deeper
if (typeof obj2[x] === 'object') {
if (obj1[x] === void 0) {
obj1[x] = {};
}
mergeObjects(obj1[x], obj2[x]);
} else {
obj1[x] = obj2[x];
}
});
return obj1;
}
const getObject = () => {
return {
a: {
b: {
c: 1,
d: 2,
}
},
e: 3
}
}
const modifiedD = mergeObjects(getObject(), {
a: {
b: {
d: 4,
},
},
});
console.log(modifiedD);
WARNING, the function I have made mutate the object which may not be the best answer
Or call it only once and then set the keys one by one like :
const getObject = () => {
return {
a: {
b: {
c: 1,
d: 2,
}
},
e: 3
}
}
const modifiedD = getObject();
modifiedD.a.b.d = 4;
console.log(modifiedD);
Further to my previous answer, as Grégory NEUT pointed out you could have a lot larger complexity.
If so, you could simply create two objects and then merge them. I found a function code snippet to be able to do that using Object.assign
Example:
const getObject = () => {
return {
a: {
b: {
c: 1,
d: 2,
}
},
e: 3
}
}
var modifiedD = getObject();
var newD = {
a: {
b: {
d: 4
},
y: 1
},
z: 20
}
/** TAKEN FROM https://gist.github.com/ahtcx/0cd94e62691f539160b32ecda18af3d6 **/
// Merge a `source` object to a `target` recursively
const merge = (target, source) => {
// Iterate through `source` properties and if an `Object` set property to merge of `target` and `source` properties
for (let key of Object.keys(source)) {
if (source[key] instanceof Object) Object.assign(source[key], merge(target[key], source[key]))
}
// Join `target` and modified `source`
Object.assign(target || {}, source)
return target
}
modifiedD = merge(modifiedD, newD);
console.log(modifiedD);
You can try the following:
getParentObj(path, obj) {
return path.split('.').reduce((o,i)=>o[i], obj);
}
const parent = getParentObj('a.b', getObject());
parent[d] = 24;
I have two input objects.
1.
var inputData1 = {
'st1': {
'name': 'k1',
'rollNo': 'abc'
},
'st2': {
'name': 'k2',
'rollNo': 'pqr'
}
};
2.
var inputData2 = {
'result': {
'data': 'sdg'
}
};
How to put first object into the other object. This is the expected output :
Output Object
var output = {
'result': {
'data': 'sdg',
'st1': {
'name': 'k1',
'rollNo': 'abc'
},
'st2': {
'name': 'k2',
'rollNo': 'pqr'
}
}
};
let say this is your first object.
var inputData1 = {
'st1':{'name':'k1', 'rollNo':'abc'},
'st2': {'name':'k2', 'rollNo':'pqr'}
};
and this is second object
var inputData2 = {
'result': {
"data": "sdg"
}
};
you can use simply like this
Object.assign(inputData2.result, inputData1);
console.log(inputData2);
What you want to do is the following :
// returns a new independant copy from the inputs
function combine(inputData1, inputData2) {
if (!inputData1 instanceof Object || !inputData2 instanceof Object) {
return {};
}
let output = Object.assign({}, inputData2);
Object.assign(output.result, inputData1);
return output;
}
var output = combine(inputData1, inputData2);
Well first off you can't have duplicate keys in an object. I suggest numbering them, and then you can just assign the contents of st1, st2 to the matchibng props in the output onject:
var inputData1 = {
'st1':{'name':'k1', 'rollNo':'abc'},
'st2': {'name':'k2', 'rollNo':'pqr'}
};
var inputData2 = {
'result': {
"data": "sdg"
}
}
So now you want to use Object.assign(obj, obj) to combine your objects:
var output = Object.assign(inputData1.result, inputData2);
This will combine your objects and give you what you want I hope. This method was added in ES6.
Let me know if this works, and remember to pick a winning answer :P
Not sure how to word this, I basically want to extend my class properties so that users can override them. However I'd prefer the anonymous function which extends the properties to just self-execute.
var extend = function(target, sources) {
if (!sources.length) return target;
const source = sources.shift();
if (isObject(target) && isObject(source)) {
for (const key in source) {
if (isObject(source[key])) {
if (!target[key]) Object.assign(target, { [key]: {} });
extend(target[key], source[key]); // <-- Line 9
} else {
Object.assign(target, { [key]: source[key] });
}
}
}
return extend(target, sources);
}.call(this, this, [ defaults, options ]);
This appears to work up until line 9 (see comment above in code). It can't seem to reference itself. However this seems to work fine if this is a named function rather than anonymous.
How can I get this to work?
Why you need it to be anonymous? Debugging gets worse. You can still use a function expression.
const myfunc = function nameFunctionExpression(i) {
if(i === 10) {
return i;
}
nameFunctionExpression(i + 1);
};
myfunc(0);
I am not sure about what you are trying to accomplish. Provide a way to override properties? Maybe post an usage example?
function extend(obj, rest) {
return Object.assign(obj, rest);
}
const obj = { a: 1, nested: { c: 3 } };
// { a: 2, nested: { c: 4 }, b: 3 }
console.log(extend(obj, { a: 2, b: 3, nested: { c: 4 } }));
I want to use JavaScript to convert an object into a query string.
For example, I want to convert:
{
a: 'hello',
b: {
b1: 'my',
b2: 'friend',
b3: {
c: 90
}
}
}
to:
?a=hello&b%5Bb1%5D=my&b%5Bb2%5D=friend&b%5Bb3%5D%5Bc%5D=90
I have found quite a few answers to this here: Flatten a javascript object to pass as querystring , but they don't seem to deal with the issue of associative arrays (or objects within objects).
I found a good answer for JQuery which works fine by using jQuery.param, but i would like an answer using either native JS or Underscore.js.
How can I do this?
you can do this:
let obj = {
a: 'hello',
b: {
b1: 'my',
b2: 'friend',
b3: {
c: 90
}
}
}
function getQueryString(obj, encode) {
function getPathToObj(obj, path = []) {
let result = [];
for (let key in obj) {
if (!obj.hasOwnProperty(key)) return;
//deep copy
let newPath = path.slice();
newPath.push(key);
let everyPath = [];
if (typeof obj[key] === "object") {
everyPath = getPathToObj(obj[key], newPath);
} else {
everyPath.push({
path: newPath,
val: obj[key]
});
}
everyPath.map((item) => result.push(item))
}
return result;
}
function composeQueryString(paths) {
let result = "";
paths.map((item) => {
let pathString = "";
if (item.path.length > 1) {
pathString = item.path.reduce((a, b, index) => {
return a + '['+ b +']';
})
} else {
pathString = item.path[0];
}
if (result) {
pathString = "&" + pathString + '=' + item.val;
} else {
pathString = "?" + pathString + '=' + item.val;
}
result += pathString;
});
return result;
}
const str = composeQueryString(getPathToObj(obj));
return encode === true ? encodeURI(str) : str;
}
console.log(getQueryString(obj, true));
get: ?a=hello&b%5Bb1%5D=my&b%5Bb2%5D=friend&b%5Bb3%5D%5Bc%5D=90
I highly recommend not trying to reinvent existing wheels. Your own implementation is probably going to be much less flexible and way more error-prone (have you thought about encoding the query string parameters correctly, for instance?) Instead, take a look at the query-string module.
With Axios you can easily achieve this:
const instance = axios.create({
url: '/user',
baseUrl: 'https://my-api-server'
});
const config = {
params: {
a: 'hello',
b: {
b1: 'my',
b2: 'friend',
b3: {
c: 90
}
}
}
}
const uri = instance.getUri(config)
document.write(uri)
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
For more information you can visit this answer.
Good Luck...