I'm trying to build a javascript object to be passed along to MongoDB. This database uses an operator $or which lets you search for multiple keys in a property at once.
Because of this, the $or operator cannot be in quotation marks, because it is actually an operator in the lookup function.
For reference, the lookup function looks like this:
db.inventory.find( { price:1.99, $or: [ { qty: { $lt: 20 } }, { sale: true } ] } )
I have found a way to 'build' an $or property out of some variables, but for some reason the property name keeps being treated as though it's a string. Here's my code:
var criteria = { $or: []};
properties.forEach(function(property) {
var propParts = property.split('=');
if(!(propParts[1].indexOf('&') === -1)){
var value = propParts[1].match(/[-'"\w\s]+/g);
for (var i = 0; i<value.length; i++){
var tempObj = {};
tempObj[propParts[0]] = value[i];
criteria.$or.push(tempObj);
}
}else{
criteria[propParts[0]] = new RegExp(propParts[1], "i");
}
});
if(criteria.$or.length<=0){
delete criteria.$or;
}
console.log(criteria);
In my console, this is being output as:
{ '$or': [ { designer: 'abc' }, { designer: 'def' } ] }
I am using similar code elsewhere (with $push instead of $or) and it works just fine, I'm declaring it in exactly the same way.
Is it possible to ensure that the property isn't being treated as a string? How can I achieve this here?
Javascript object keys are always coerced to strings. That you can specify some keys without quotes in object literals {key: 0} or using dot notation object.key = 0 is merely syntactic sugar for keys that also happen match the production for identifiers. '$or' is a valid javascript identifier, so you can say either:
{$or: [...]}
or
{"$or": [...]}
or
{'$or': [...]}
and they're all equivalent.
Related
I'm a programming beginner.
API post call accepts object variable (derived from variable) as a string as follows
"option":
{
"235": “30”
},
{
"238": “32”
}
My code angular 6
option = [];
---
this.option.push({
[option.product_option_id]: $event
});
which result
option = [ {
235: 30
}]
but need this variable in double-quoted "235".
please help
but need this variable in double-quoted "235"
By which you mean that you need it to be a string.
Don't worry, it is. When you use a number as a property name, it's converted to a string automatically. Property names can only be strings or Symbols, so things that aren't strings or Symbols get converted to string:
class Example {
constructor() {
this.option = [];
const option = {
product_option_id: 42
};
const $event = {};
this.option.push({
[option.product_option_id]: $event
});
const pushed = this.option[0];
for (const key of Object.keys(pushed)) {
console.log(`${key}: ${typeof key}`);
}
}
}
new Example();
That said, the expression within the [] of a computed property name is just that: an expression. So if you wanted to be explicit, you could use String there:
option.push({
[String(option.product_option_id)]: $event
});
I want to check if an object already exists in a given object by only having the object.
For instance:
const information = {
...
city: {
Streetname: ''
}
}
Now, I get the city object and want to check if it is already in the information object (without knowing the property name). The city could be n deep in the information object.
To get the property name of an object you can use Object.keys(). The first problem solved.
Now we need to iterate through the whole object including nested objects. This is the second problem.
And compare it to a query object. This is the third problem.
I assume that we have an object that only contains "simple" though nested objects with primitive values (I do not consider objects with functions or arrays)
// let's assume we have this object
const information = {
city: {
Streetname: 'streetname1'
},
house: {
color: "blue",
height: 100,
city: {
findMe: { Streetname: '' } // we want to get the path to this property 'findMe'
}
},
findMeToo: {
Streetname: '' // we also want to get the path to this proeprty 'findMeToo'
},
willNotFindMe: {
streetname: '' // case sensetive
}
}
// this is our object we want to use to find the property name with
const queryObject = {
Streetname : ''
}
If you use === to compare Objects you will always compare by reference. In our case, we are interested to compare the values. There is a rather extensive checking involved if you want to do it for more complex objects (read this SO comment for details), we will use a simplistic version:
// Note that this only evaluates to true if EVERYTHING is equal.
// This includes the order of the properties, since we are eventually comparing strings here.
JSON.stringify(obj1) === JSON.stringify(obj2)
Before we start to implement our property pathfinder I will introduce a simple function to check if a given value is an Object or a primitive value.
function isObject(obj) {
return obj === Object(obj); // if you pass a string it will create an object and compare it to a string and thus result to false
}
We use this function to know when to stop diving deeper since we reached a primitive value which does not contain any further objects. We loop through the whole object and dive deeper every time we find a nested object.
function findPropertyPath(obj, currentPropertyPath) {
const keys = isObject(obj) ? Object.keys(obj) : []; // if it is not an Object we want to assign an empty array or Object.keys() will implicitly cast a String to an array object
const previousPath = currentPropertyPath; // set to the parent node
keys.forEach(key => {
const currentObj = obj[key];
currentPropertyPath = `${previousPath}.${key}`;
if (JSON.stringify(currentObj) === JSON.stringify(queryObject)) console.log(currentPropertyPath); // this is what we are looking for
findPropertyPath(currentObj, currentPropertyPath); // since we are using recursion this is not suited for deeply nested objects
})
}
findPropertyPath(information, "information"); // call the function with the root key
This will find all "property paths" that contain an object that is equal to your query object (compared by value) using recursion.
information.house.city.findMe
information.findMeToo
const contains = (item, data) => item === data || Object.getOwnPropertyNames(data).some(prop => contains(item, data[prop]));
const information = {
city: {
Streetname: ''
}
}
console.log(contains(information.city, information));
console.log(contains({}, information));
I have javascript object which look like this:
{ name: 'Barney', color: 'blue', parent: {name: 'Henry'} }
When I use $filter('filter')('Henry') on an array which includes the object above, I don't want it to be included as a result. I only want to filter out things matching on the first level, in this case the 'name' and 'color' properties.
Is it possible?
You'd want to create a custom filter since the default filter provided by Angular appears to do a deep comparison.
Here's an example I came up with real quick, you may want to change the filter to suit your needs:
// Looks like a nice little tree :)
app.filter('shallowFilter', function () {
return function (items, value) {
if (!angular.isDefined(value) || value === '') {
return items;
}
return items.filter(function (item) {
for (var prop in item) {
if (item.hasOwnProperty(prop)) {
var propVal = item[prop],
propLower,
valLower;
// Skip values that are not a string..
if (typeof propVal !== 'string') {
continue;
}
propLower = propVal.toLowerCase();
valLower = value.toLowerCase();
if (propLower.indexOf(valLower) !== -1) {
return true;
}
}
}
});
};
});
Here's a plunker demonstrating how it works.
Edit:
This will only loop over the "low level" properties of an object (shallow search), which is what I assume you want.
Use the object notation:
From documentation:
Object: A pattern object can be used to filter specific properties on objects contained by array. For example {name:"M", phone:"1"} predicate will return an array of items which have property name containing "M" and property phone containing "1". A special property name $ can be used (as in {$:"text"}) to accept a match against any property of the object. That's equivalent to the simple substring match with a string as described above. The predicate can be negated by prefixing the string with !. For Example {name: "!M"} predicate will return an array of items which have property name not containing "M".
$filter('filter')({ name: 'Henry' });
I have the following that i entered into the mongo terminal and it works great
db.cars.update({'_id':'FordXdfg'},{$inc :{'attribs.0.totl':1}})
which basically updates an array using dot notation, the 0 is the index of the array.
this does work. but transferring it to node my 0 comes from a variable.
so i tried
var carIndex = 3;
cars.update({'_id':'FordXdfg'},{$inc :{'attribs.' + carIndex + '.totl':1}}, function (err, callback) ................)
seems to be invalid javascript, if i replace my carIndex with 3 then it works i.e.
cars.update({'_id':'FordXdfg'},{$inc :{'attribs.3.totl':1}}, function (err, callback) ................)
Any ideas?
thanks
When using that style of object initialization in JavaScript, property names must be string literals. When using the object initialization syntax, property names can not be constructed at run time in code. For example, you can only use literals like:
{
"name": "Martin"
"location": "Earth"
"value": 1234
}
You cannot do this:
var propName = "name";
var obj = {
propName: "Martin";
};
While it syntactically appears to work, you'll end up with an object that looks like:
{
propName: "Martin"
}
Again, that's because only literal values are accepted when constructing an object using the shortened syntax. It will not interpret variable values.
There are two other options for setting properties of a JavaScript object, either through simple dot-notation:
obj.name = "Martin";
Or, you can use bracket notation:
obj["name"] = "Martin";
As objects in JavaScript act like associative arrays in that you can define new properties/keys at runtime each with a value, either syntax above works, and both result in the same underlying storage (and can be used interchangeably).
So, you'll need to construct the $inc syntax separately using the other technique for setting object property values in JavaScript:
var inc = {};
inc["attribs." + carIndx + ".totl"] = 1;
Then use that inside of your update:
{ $inc: inc }
I'm trying to create an object that contains an object, so think of it as a dictionary:
var dictionaries = {};
dictionaries.english_to_french =
{
{english:"hello",french:"bonjour"},
{english:"i want",french:"je veux"},
{english:"bla",french:"le bla"}
};
but it gives the error Uncaught SyntaxError: Unexpected token {
what am I doing wrong?
Thanks !
Edit
I'm sorry that I did not clarify what I want to do.
Edited the code above.
You're trying to give your object a property, and that property will be a single object:
dictionaries.english_to_french =
{english:"hello",french:"bonjour"}
;
You don't need the extra { }. You could declare the whole thing at once:
var dictionaries = {
english_to_french: {
english: "hello", french: "bonjour"
}
};
I would suggest that a better format for your dictionaries might be:
var dictionaries = {
english_to_french: {
"hello": "bonjour",
"chicken": "poulet", // ? something like that
"Englishman": "rosbif"
}
};
That way you can look up words directly without having to search. You could then create the reverse dictionary from that:
dictionaries.french_to_english = function(dict) {
var rv = {};
for (var eword in dict)
rv[dict[eword]] = eword;
return rv;
}(dictionaries.english_to_french);
In order to nest two or more objects, the objects need to have an attribute assigned to them. For example,
{
"hello":{
"english":"hello",
"french":"bonjour",
"portuguese":"ola"
},
"good day":{...},
"how are you":{...}
}
"hello" at the beginning of the object would be the attribute. Then the object is its value. So that way you can access the object by accessing its attribute. Just putting an object in an object does not work. That's why you're getting your error.