I have a unique case where I need to use a Javacript symbol as an object's key. This is necessary because in order to conform to Sequelize's documentation, there are instances where we need to have something that looks like this:
const where = {
cost: {
[Op.gt]: 1000,
[Op.lt]: 2000
}
}
Both [Op.gt] and [Op.lt] are Javascript symbols that assist with querying. The block of code will query where a property called cost is greater than 1000 but less than 2000. But when I try to programmatically set the key/value pairs like:
where['cost'][[Op.gt]] = 1000;
I receive the following error:
Cannot convert a Symbol value to a string
This is a dynamic object, so I cannot hard code the symbols into the where query since the next user may not need to query by these parameters. How do I go about this? Thanks!
Remove 1 bracket around your symbol and you will be fine:
where['cost'][Op.gt] = 1000;
obj[Op.gt] means you're accessing an object property with the Op.gt name. obj[[Op.gt]] means you're accessing an object property with the name equal to an array [Op.gt] stringified. Which is similar to below:
const arr = [Op.gt];
const propertyName = arr.toString(); // => throw error "Cannot convert a Symbol value to a string"
where['cost'][propertyName];
Related
I'm trying to create a really simple IndexedDB with some JavaScript, but it fails in the on handler already. Apparently the browser (Chrome 57) is not able to parse the keyPath (in Basic Concepts) of my storage.
I'm following more or less these simple examples: MDN or Opera-Dev.
Suppose I want to store objects like this one in the DB:
{
"1": 23, // the unique id
"2": 'Name',
"3": 'Description',
"4": null,
"5": null
}
Here is the code:
var sStoreNodes = 'nodes';
var sIdFieldNode = '1'; // the important part
// event is fired for creating the DB and upgrading the version
request.onupgradeneeded = function(event)
{
var db = event.target.result;
// Create an objectStore for nodes. Unique key should be the id of the node, on property 1.
// So ID will be the key!
var objectStore = db.createObjectStore(
sStoreNodes,
{
// changing to a plain string works, if it is a valid identifier and not just a strigified number
'keyPath' : [ sIdFieldNode ],
'autoIncrement' : false // really important here
});
};
The error message reads like:
Uncaught DOMException: Failed to execute 'createObjectStore' on 'IDBDatabase': The keyPath option is not a valid key path.
at IDBOpenDBRequest.CCapIndexedDB.request.onupgradeneeded
I can also try to leave out the key path, but I'm wondering why this happens and want can I do about it, if I really need to use a (complex) key path.
Regarding the spec:
I'm not sure, whether this can be applied here:
A value is said to be a valid key if it is one of the following ECMAScript [ECMA-262] types: Number primitive value, String primitive value, Date object, or Array object.
and what this actually means:
If the key path is a DOMString, the value [for getting the key path] will be a DOMString equal to the key path. If the key path is a sequence, the value will be a new Array, populated by appending Strings equal to each DOMString in the sequence.
Edit This works, if you don't use a stringified number, but a string instead, which is a valid identifier (beginning with a character [a-zA-Z]). So 'keyPath' : 'b' is OK. I guess this is because this value is used for creating paths like a.b.c.
Here is the definition of a key path, from the spec:
A key path is a DOMString or sequence that defines how to extract a key from a value. A valid key path is one of:
An empty DOMString.
An identifier, which is a DOMString matching the IdentifierName production from the ECMAScript Language Specification [ECMA-262].
A DOMString consisting of two or more identifiers separated by periods (ASCII character code 46).
A non-empty sequence containing only DOMStrings conforming to the above requirements.
For a string containing an integer, clearly the first, third, and fourth options do not apply. For the second, we have to see what an IdentifierName is, which is a little complicated, but basically it has to start with a letter, underscore, or dollar sign. This means that a string containing just an integer is not a valid key path.
If you really do have an object where the primary key is in a field whose name is a string containing an integer, you can either rename the field or not use key paths (in which case you have to manually specify the key as the second argument to IDBObjectStore.add and IDBObjectStore.put).
You linked to the definition for a key, which defines the valid values that a key can have (like for an object {a: 1} where the key path is 'a' the key is 1, which is valid).
The other thing you linked to is about key paths like a.b.c referencing {a: {b: {c: 1}}}.
I have a variable called uids
var uids = [];
Then I write some value to it property
uids[16778923] = "3fd6335d-b0e4-4d77-b304-d30c651ed509"
But before it
if (!uids[user.id]) {
uids[user.id] = generateKey(user);
}
This thing behaves ok. If I try to get the value of it property
uids[currentUser.id]
It will give me a value of this property. If I try to call some methods like
Object.keys(uids);
It will give me, what I expected. And here the mystery comes...
uids;
RAM rest in piece. See the node eating ram
I am very confused now. What's wrong?
This is because you are creating a huge array and node will reserve memory for it - who knows what comes. I'd say that's a scenario where you would use a Map (or a plain object, but Map feels better here.
var uids = new Map();
var key = 456464564564654;
if (! uids.has(key)) {
uids.set(key, generateKey(user))
}
You are creating an empty array (length is zero), then you assign some value to an arbitrary index. This will make the array grow as big as the index and assign the value to that index. Look at this example using node.js REPL:
> var a = []
undefined
> a[5] = "something"
'something'
> a
[ , , , , , 'something' ]
> a.length
6
Instead of creating an array, you could create a Map() or an common javascript object (singleton). Javascript objects behave like Maps but only Strings can be used as keys. If you assign a Number to be key, javascript will convert it to String automatically.
Personally, I would go with objects because they perform better. Instantiating an object takes longer than instantiating a Map (and it doesn't seem like you need to create several groups of "uids"), but once done, adding new keys and retrieving values from any key in faster when using common objects. At least that's how things go in my node.js v6.7.0 on ubuntu 14.04 but you could try for yourself. And it would also make the least alteration to your code.
var uids = {} // common/ordinary empty javascript object instead of array.
if (!uids[user.id]) { // getting value from one key works the same.
uids[user.id] = generateKey(user) // assignment works the same.
}
////
uids[16778923] = "3fd6335d-b0e4-4d77-b304-d30c651ed509" // key will be "16778923".
uids[16778923] // getting value for key "16778923" can be done using 16778923 instead of "16778923".
////
uids[currentUser.id] // still returning values like this.
Object.keys(uids) // still returning an array of keys like this. but they are all Strings.
I have a confusion of what this array can hold. Also, I want to know how it assigns the values to the variable set.
Can someone give me an example of data['sax'] please and explain me the loop below?
for(var x = 0; x < data['sax'].length; x++){
var set = data['sax'][x];
Then what does this mean ?
id : set.saxID,
name : set.symbol
What you have here is an array that is being looped through. data['sax'] will be something along the lines of the following:
var data = {
sax: [
{
saxID: 1,
symbol: 1
},
{
saxID: 2,
symbol: 2
}
]
}
As you can see in the example above, sax is an array which contains multiple objects. What happens when you loop over it, is that it accesses one of the objects inside the array. So data['sax'][0] will give you the object with saxID: 1.
Using the variable set to temporarily store the data in; you can access the data of data['sax'][0] as set. So data['sax'][0].saxID becomes set.saxID. It is somewhat of a shorthand version of accessing the data.
What happens with id: set.saxID, is that the values get assigned to a new object. Which will be something like the following.
var newSax = {
id: set.saxID
}
You are basically transferring data from one object to another.
Explaining the code
var set = data['sax'][x];
Here you are creating a variable called set and assigning it a value from data['sax'][x].
data['sax'] this might look like a array But Its Not, to access value of a array we use index but here its a string. This is another way of accessig a property value of a object. So data is a object with one of its property being sax. That means
data = {sax: somevalue};
Now we have,
data['sax'][x] So as you know data['sax'] is a object and then this [x] , here x is not a string its a variable and it holds the Number value (0,1,2,3...) , it means your data['sax'] value is a array since we are accessing from index.
So from the above analysis your data['sax'] will be in this format.
Array
data = { sax : ["someValue","someValue"]}
So variable set will be
var set = "someValue"; // if x = 0, as in the first loop
Now coming to the code
id : set.saxID,
name : set.symbol
set.saxID this is used if the set is an object. In Jquery to retrieve the value of a property in the object you use the . operator with property name (or by the property name as seen above). So the code means set is a object with two properties saxID and symbol. So your set object will be like
set = { saxID: 123, symbol = "TEST"}
That also means that your data value be
data = { sax : [{saxID: 123, symbol = "TEST"},{saxID: 123, symbol = "TEST"}]}
Let me know if I was clear
I have String like below.
10=150~Jude|120~John|100~Paul#20=150~Jude|440~Niroshan#15=111~Eminem|2123~Sarah
I need a way to retrieve the string by giving the ID.
E.g.: I give 20; return 150~Jude|440~Niroshan.
I think I need a HashMap to achieve this.
Key > 20
Value > 150~Jude|440~Niroshan
I am looking for an pure JavaScript approach. Any Help greatly appreciated.
If you're getting the above string in response from server, it'll be better if you can get it in the below object format in the JSON format. If you don't have control on how you're getting response you can use string and array methods to convert the string to object.
Creating an object is better choice in your case.
Split the string by # symbol
Loop over all the substrings from splitted array
In each iteration, again split the string by = symbol to get the key and value
Add key-value pair in the object
To get the value from object using key use array subscript notation e.g. myObj[name]
var str = '10=150~Jude|120~John|100~Paul#20=150~Jude|440~Niroshan#15=111~Eminem|2123~Sarah';
var hashMap = {}; // Declare empty object
// Split by # symbol and iterate over each item from array
str.split('#').forEach(function(e) {
var arr = e.split('=');
hashMap[arr[0]] = arr[1]; // Add key value in the object
});
console.log(hashMap);
document.write(hashMap[20]); // To access the value using key
If you have access to ES6 features, you might consider using Map built-in object, which will give you helpful methods to retrieve/set/... entries (etc.) out-of-the-box.
I try to dynamically access a field in mongo database.
The target field is given by this line:
var contextTarget= Session.get('contextLayout')+"BackgroundColor";
Where
Session.get('contextLayout') could be either a string or an _id of a collection(eg. userHomeBackgroundColor or 56xhPYg8w3Qtv9xPLBackgroundColor).
I don't know how to access the value.
I tried this code:
var contextTarget= Session.get('contextLayout')+"BackgroundColor";
var tempStore ={};
tempStore['target']=contextTarget;
var newTarget = tempStore.target;
console.log(newTarget);//return the correct value
var color = customConfiguration.findOne({'author':auth}).newTarget;
console.log(color);//return undefined
It doesn't work. I suppose that it's because the newTarget variable is a string because if I directly write the expected result in the console, it works.How can I do?
EDIT.
like said BraveKenny:
var color = customConfiguration.findOne({'author':auth})[contextTarget];
In fact, it was not necessary to pass by the object tempStore. With the square brackets keys are properly sent. I had already try it but with a dot before the square bracket.
#Michael: For the schema, I don't have a schema for this collection, because the key names are dynamically created.
Thanks a lot.
If I understand correctly, in the end, you want to get a variable attribute of your customConfiguration object, which name is contained in newTarget.
Maybe try this instead:
var color = customConfiguration.findOne({'author':auth})[newTarget];
Basically when you type:
someObject.toto = 'someValue';
var someAttribute = 'toto';
console.log(someObject.someAttribute); //undefined
console.log(someObject[someAttribute]); // 'someValue'
JavaScript will always assume someAttribute is an attribute of someObject when it's called with a dot notation. Not a variable set in the scope. If you want the value of an attribute toto which is contained in a string variable (in my case, someAttribute), you have to pass this variable as an index, like this:
console.log(someObject[someAttribute]); // 'someValue'
It would be helpful if you could describe the schema of the collection customConfiguration. I just guess that it contains a collection of documents with just one property each (e.g. userHomeBackgroundColor or 56xhPYg8w3Qtv9xPLBackgroundColor) and you are interested in the one document with the correct property name to read the property value. So I guess, in JSON notation the collection looks something like this:
[
{"userHomeBackgroundColor": "red"},
{"56xhPYg8w3Qtv9xPLBackgroundColor": "green"},
{"someOtherConfigProperty" : "someOtherValue"}
]
If this assumption is correct, I would replace your original line
var color = customConfiguration.findOne({'author':auth}).newTarget;
With something like this:
var selector = {}; // Empty selector object for findOne
selector[contextTarget] = // Add dynamic property and as its value
{$exists: true}; // an object with an $exists-property
// with value true
// now find the (first) configuration document having the dynamic property
var configDocument = customConfiguration.findOne(selector);
// read the value of the dynamic property of the configuration document
var color = configDocument[contextTarget];
But to verify this, I need more background about the schema of the collection customConfiguration. You might also want to check the documentation of $exists (http://docs.mongodb.org/manual/reference/operator/query/exists/#op._S_exists) and findOne() (http://docs.mongodb.org/manual/reference/method/db.collection.findOne/) in the documentation.
If, however, the collection customConfiguration contains one configuration document for each author, change your line to just this:
var color = customConfiguration.findOne({'author':auth})[newTarget];
This solution has been described in a previous answer.