I am creating a dynamic javascript object to query from the mongodb, and my code as follows,
_.each(separatedFilter, function (str) {
const filter = str.split('=');
console.log('filter is', JSON.stringify(filter));
if (filter[1] && filter[1].trim() !== '') {
var key = `${filter[0]}`;
var obj = {};
obj[key] = filter[1];
if (key = 'date.start:{$gte') {
key = '"date.start":{"$gte"';
}
if (key = 'date.end:{$lt') {
key = '"date.end":{"$lt"';
}
query.push(obj);
}
});
the above code creates a object as follows,
{ '$and':
[ { name: [Object] },
{},
{ '"date.start":{"$gte"': '2016-12-18T18:30:00.000Z',
'"date.end":{"$lt"': '2016-12-18T18:30:00.000Z' }
] }
in the above object i could see ' at the start and end of date.start and date.end. whereas i just want it to be,
{ '$and':
[ { name: [Object] },
{},
{ "date.start":{"$gte": '2016-12-18T18:30:00.000Z',
"date.end":{"$lt": '2016-12-18T18:30:00.000Z' }
] }
You cannot define a value (the nested object) via a key, nor can you partially define an object with a key (i.e. $gte) but without a value.
A fix would look something like this:
if (key === 'date.start:{$gte') {
obj['date.start'] = { '$gte' : null };
}
if (key === 'date.end:{$lt') {
obj['date.start'] = { '$lt' : null };
}
obj[key] = filter[1];
This assumes that null will be replaced with the appropriate timestamps somewhere else in the program and then you can serialize that object to a string before sending the query.
Explanation
These lines have a number of problems:
if (key = 'date.start:{$gte') {
key = '"date.start":{"$gte"';
}
if (key = 'date.end:{$lt') {
key = '"date.end":{"$lt"';
}
They are inserting quotes into the key for some reason. Probably just a mistake.
They are assigning to key within the conditional statement, rather than doing a comparison. This will have the effect of always executing the code within the if block, because the string being assigned and returned is always truthy.
They come after the key and its value are already assigned to the object via obj[key] = filter[1]. Since the assignment has already run by the time the if statements do, they have no chance to affect the key that is used in the assignment.
Also, this line:
var key = `${filter[0]}`;
Can be simplified, as it is the same as:
var key = filter[0];
Or even better with let and destructuring:
let [key] = filter;
Related
I want to destructure a dynamic key from a object, where `key` is some pattern. There is counter appended to key.
var obj = {
"key2":{"left":{"content": "This data to be pulled"}},
"hasErrorcharges2":false,
"hasErrorcharges2_Card":""
}
const {key2: {left: content }} = obj;
Here key2 is dynamic. So we know that it will always start with key and the other values can be key0, key1, key3 and hence forth. How do we traverse in this case?
Things tried.
Match the if object has any key similar to it. and then return the matched key. but got true false
can't destructure dynamic prop. but in this we know a pattern
traverse through the object with dynamic property and get the value.
expecting to write a similar function like hasOwn() or hasOwnProperty
You can't do the destructuring until you know the name of the property. You can find it by using find on Object.keys (but keep reading for an alternative). Then you can use computed property notation to specify that name in the destructuring expression. (There's also a small error in that expression, see the highlighted bit below.)
const keyName = Object.keys(obj).find((key) => key.startsWith("key"));
if (keyName) {
const {
// vvvvvvvvv−−−−−−−−−−−−−−−−−−−−−−−−−− computed property notation
[keyName]: { left: { content } },
// ^−−−−−−−−−^−−−−− minor correction to destructuring
} = obj;
// ...
}
There I've just checked that the property name starts with key — you might want to beef up the condition in the find callback, but that's the general idea.
Live Example:
const obj = {
key2: { left: { content: "This data to be pulled" } },
hasErrorcharges2: false,
hasErrorcharges2_Card: "",
};
const keyName = Object.keys(obj).find((key) => key.startsWith("key"));
if (keyName) {
const {
[keyName]: { left: { content } },
} = obj;
console.log(`content = ${content}`);
}
That said, if you need to loop through the object properties anyway, it may not be worth setting yourself up for destructuring vs. just grabbing the property in a loop and breaking when you find it:
let content = null;
for (const key in obj) {
if (key.startsWith("key")) {
content = obj[key].left.content;
break;
}
}
if (content !== null) { // Valid only if we know content won't be `null` in the object
// ...
}
Live Example:
const obj = {
key2: { left: { content: "This data to be pulled" } },
hasErrorcharges2: false,
hasErrorcharges2_Card: "",
};
let content = null;
for (const key in obj) {
if (key.startsWith("key")) {
content = obj[key].left.content;
break;
}
}
if (content !== null) { // Valid only if we know content won't be `null` in the object
console.log(`content = ${content}`);
}
If you like, this:
content = obj[key].left.content;
could be:
({ content } = obj[key].left);
...which avoid repeating the identifier content. Or even:
({left: { content }} = obj[key]);
...though there's really no need to use the nested destructuring, it doesn't save you anything. :-)
(We need the () around it because otherwise the { at the beginning looks like the beginning of a block to the JavaScript parser.)
I am trying to write a javascript recursive function that receives one parameter - nested JSON object.
The function goes through the potentially infinitely nested object and converts all the keys (property names) to a string that is stored in array. Array is returned to a place where the function was called.
Example of JSON object:
{
OBJECT1: {
ATTRIBUTE3: {
PARAMETER2: {
PROPERTY1: {
}
}
}
}
}
The object does not hold any values.
What i tried and did not work:
function convertKeysToString(obj) {
let keys = [];
for (let key in obj) {
if (typeof obj[key] === 'object') {
keys = keys.concat(convertKeysToString(obj[key]));
} else {
keys.push(key.toString());
}
}
return keys;
}
As a result, I expected that returned key is pushed to an array, but the funciton didnt get the key at all or was not pushed to keys array.
Another code I tried:
function getNestedObjectKeys(obj) {
var keys = []
var firstLevel = null
var property = Object.keys(obj)
property = property[0]
firstLevel = Object.keys(obj[property])[0]
if (firstLevel == undefined) {
return 0
}
let returnedValue = keys.unshift(getNestedObjectKeys(obj[property]))
if (returnedValue == 0) {
return Object.keys(obj[property])[0]
}
returnedValue = Object.keys(obj[property])[0]
if (returnedValue != obj[property[0]]) {
return Object.keys(obj[property])[0]
}
else if (returnedValue == firstLevel) {
return keys
}
}
The function should return the key name and push (unshift) it to string and then return it, but the unshift doesnt do what I expect and in the returnedValue is not a expected returned string.
I approached it the way that the function findd the deepest (empty) object, and starts returning the name of the key. The thing is that I must return the key name AND push it to the string, which I can't find the way to accomplish at once.
Your first solution is pretty close, but has one problem (well, one main problem): when the value is type object, you don't add its key to the array. So how is it supposed to get into the array? Give this a shot:
function convertKeysToString(obj) {
let keys = [];
for (let key in obj) {
keys.push(key.toString());
if (typeof obj[key] === 'object') {
keys = keys.concat(convertKeysToString(obj[key]));
}
}
return keys;
}
Other things you may want to consider:
typeof null is object.
typeof [] is also object.
You could have a look to object, which are truthy and typeof object.
const
getKeys = object => (keys => [
...keys.flatMap(key => object[key] && typeof object[key] === 'object'
? [key, ...getKeys(object[key])]
: [key]
)
])(Object.keys(object)),
data = { OBJECT1: { ATTRIBUTE3: { PARAMETER2: { PROPERTY1: {} } } } },
result = getKeys(data);
console.log(result);
I have the following object like this:
{ where: { [Symbol(or)]: [ [Object], [Object] ] },hooks: true, rejectOnEmpty: false }
I'm calling JSON.stringify on this, and it gets converted to:
{"where":{},"hooks":true,"rejectOnEmpty":false}
I think this is because [Symbol(or)] evaluates to undefined so stringify removes it.
This value is coming from Sequelize operators, specifically Op.or. Is there a way stringify can convert this to a String so I would instead receive:
{"where":{"[Symbol(or)]": [[<<stringifiedObject>>], [<<stringifiedObject>>]]},"hooks":true,"rejectOnEmpty":false}
I know I could pass a function to JSON.stringify which would replace undefined with something, but I would like to maintain the original Symbol in the string replacement, so that I can distinguish between Symbol(and) and Symbol(or), even though both would evaluate to undefined.
Solved with the following function:
const cleanObject = (obj) => {
try {
const keys = Reflect.ownKeys(obj);
const ret = {};
keys.forEach((key) => {
let val = obj[key];
if (Object.prototype.toString.call(val) === '[object Object]') {
val = cleanObject(val);
}
if (typeof key === 'symbol') {
const newKey = `${String(key)}`;
ret[newKey] = val;
} else {
ret[key] = val;
}
});
return ret;
} catch (ex) {
console.log(ex);
}
};
Reflect.ownKeys returns all keys, including Symbols.
Next, I check if a given key is a Symbol and replace it with a string. I also call the function recursively to apply the same logic to all nested objects.
I am working on a function where I pass an object (record) to a function. It then loops over the keys and checks to see if that key is in our second object (lookup). If there is a match, it replaces the value in our record with a manipulated version (turns it into a link). If there is no match, it keeps its original value.
This is what I am passing to the function:
{ Source: "1234", NTID: "joeBob", Department: "x123", Email: 'joebob#example.com' }
-Here is the function
function createLink2(record) {
// Link types
var output = {
'ntid': 'https://example.com/profile/',
'email': 'mailTo:'
};
// Vars
var i,
key,
keys = Object.keys(output);
// Loop over our object keys
Object.keys(record).forEach(function(k, ind) {
// Loop over each of the link types
for ( i = 0; i < keys.length; ++i ) {
key = keys[i];
// If our key matches that of our object, turn it into a link
if(k.toLowerCase() == key){
record = ''+record[k]+'';
}else{
// Return the original value of the property since its not a match. Not quite sure how to do this part.
}
}
});
return record;
}
My goal here is that it would replace the value of Email with joeBob#example.com and NTID with joeBob.
The issue I am having is with the return - Not quite sure how to edit the data and return the full object back.
change this line:
record = ''+record[k]+'';
to this:
record[k] = ''+record[k]+'';
Of course, you could do this more easily by referring to the properties of the object directly:
function createLink2(record) {
// Link types
var output = {
'NTID': 'https://example.com/profile/',
'Email': 'mailTo:'
};
// Loop over the output keys
Object.keys(output).forEach(function(k, ind) {
if(record.hasOwnProperty(k)) {
record[k] = '' + record[k] + '';
}
});
return record;
}
Note that you don't really need to return it since the contents of the object will be changed directly as mentioned by others in the comments.
Javascript objects are passed by reference. So if you modify the object in the function it will be enough. Example:
function test(obj) {
obj.a = 10
}
var x = {a: 2};
test(x);
console.log(x.a) //prints 10
So, all you have to do is modify the value of "Email" with whatever you want.
You can iterate over an array and return an object using .reduce()
function createLink2(record) {
// Link types
var output = {
'ntid': 'https://example.com/profile/',
'email': 'mailTo:'
};
// Vars
var keys = Object.keys(output);
// Loop over our object keys
record = keys.reduce(function(obj, currKey) {
if (obj[currKey] != undefined) {
obj[currKey] = '' + obj[currKey] + ''
}
return obj;
}, record);
return record;
}
console.log(createLink2({ntid: "12345", email: "joebob#gmail.com"}));
ive got an object:
var car = {
company: "Honda",
year: "2011",
Model: "Brio"
}
I was wondering if there exists an inherited method (is that the right phrase?) to check if a value exists inside a given object, somewhat like x.hasOwnProperty, or if (x in car). Or, should I write my own.
I've done a few google searches, but they all either lead to hasOwnProperty or to check if a value exists inside an array.
Editing to please all the people in the comments:
There are two use cases i could think of where this would be useful:
checking for undefined keys and reporting which one
if (!car.isInvalid(car, undefined))
validCarsArray.push (car);
Checking if a general user input exists in an object
var text = searchBox.input;
validCarArrays.forEach (function (car) {
if (car.hasOwnValue(car, text)) {
displayToUserAsResult (car);
}
});
Let's say we start with
const obj = {foo : "bar"};
Check for a value:
const hasValue = Object.values(obj).includes("bar");
Check for a property:
// NOTE: Requires obj.toString() if key is a number
const hasProperty = Object.keys(obj).includes("foo");
Multi-level value:
function hasValueDeep(json, findValue) {
const values = Object.values(json);
let hasValue = values.includes(findValue);
values.forEach(function(value) {
if (typeof value === "object") {
hasValue = hasValue || hasValueDeep(value, findValue);
}
})
return hasValue;
}
Multi-level property:
function hasPropertyDeep(json, findProperty) {
const keys = Object.keys(json);
let hasProperty = keys.includes((findProperty).toString());
keys.forEach(function(key) {
const value = json[key];
if (typeof value === "object") {
hasProperty = hasProperty || hasPropertyDeep(value, findProperty);
}
})
return hasProperty;
}
No, there is no built in method to search for a value on an object.
The only way to do so is to iterate over all the keys of the object and check each value. Using techniques that would work even in old browsers, you can do this:
function findValue(o, value) {
for (var prop in o) {
if (o.hasOwnProperty(prop) && o[prop] === value) {
return prop;
}
}
return null;
}
findValue(car, "2011"); // will return "year"
findValue(car, "2012"); // will return null
Note: This will return the first property that contains the search value even though there could be more than one property that matched. At the cost of efficiency, you could return an array of all properties that contain the desired value.
Note: This uses the extra .hasOwnProperty() check as a safeguard against any code that adds enumerable properties to Object.prototype. If there is no such code and you're sure there never will be, then the .hasOwnProperty() check can be eliminated.
There is no built-in function but it can be done using Object.keys() and [].some():
function hasValue(obj, value) {
return Object.keys(obj).some((key) => obj[key] == value);
}
var car = {
company: "Honda",
year: "2011",
Model: "Brio"
}
snippet.log('car has Honda: ' + hasValue(car, 'Honda'));
snippet.log('car has NotHonda: ' + hasValue(car, 'NotHonda'));
<script src="https://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>
This function uses Object.keys() and returns an array with the keys for the object which has the given value.
The Object.keys() method returns an array of a given object's own enumerable properties, in the same order as that provided by a for ... in loop (the difference being that a for-in loop enumerates properties in the prototype chain as well).
var car = {
company: "Honda",
year: "2011",
Model: "Brio"
};
function getKeysWithValue(v, o) {
return Object.keys(o).filter(function (k) {
return o[k] === v;
});
}
document.write('<pre>' + JSON.stringify(getKeysWithValue('Honda', car), 0, 4) + '</pre>');
I used this function, to check wether or not array2 contains a common value with array1.
const array1 = ['a','b','c','x'];
const array2 = ['z','y','x'];
function ContainsCommonItem3(arr1, arr2){
return arr1.some(item => arr2.includes(item));
}