I'm trying to retrieve a nested object based on a nested nested id.
So my object is as follows
object = {
1: {
feature: {id:"1012"},
}
2: {
feature: {id:"3032"}
}
}
I have an id and I need to retrieve the corresponding object or to be more specific the object id. The object is a lot more complex but above shows the values that I need to retrieve.
I don't have much experience in JavaScript so am unsure how to achieve this. I've tried using Jquery's attribute selectors but have not been successful.
Any help would be appreciated.
Thanks.
if your "id" is mean like 1 or 2
do it like this:view it in JSFiddle
var obj = {
1: {
feature: {id:"1012"}
},
2: {
feature: {id:"3032"}
}
}
var getById = function(id){
return obj[id];
}
alert(getById(1).feature.id);
another way,if your id means like '1012','3032'
do it like this:view it in JSFiddle
my post about the Map in js
If I'm understanding the question correctly you are trying to use the id property of the object in each feature property to get the key (1, 2, etc) from object? So if you entered "1012" you would get back 1, if you entered "3032" you would get 2, etc?
If so this would do it:
var object = {
1: {
feature: {id:"1012"}
},
2: {
feature: {id:"3032"}
}
},
getIdByFeatureId = function (featureId) {
var id,
subObject;
// loop through each property of the object
for (id in object) {
// protect ourselves in case someone has tampered with Object.prototype
if (object.hasOwnProperty(id)) {
subObject = object[id];
if (subObject.feature.id === featureId) {
return id;
}
}
}
// none found? return null.
return null;
};
getIdByFeatureId("3032"); // returns 2
getIdByFeatureId("1012"); // returns 1
getIdByFeatureId("90210"); // returns null
You can play with the code in this fiddle.
Numbers stored as strings can be a pain, and often lead to confusion in how one need to call a function like this. One thing you might notice is I used the === strict equal operator. This only returns true if both values are exactly the same, including their type. It's good practice to use strict comparison operators unless you absolutely can't. It is also slightly faster since it doesn't have to coerce the values into a like type. But that means that you must pass a string into the function in order for it to match. You could use the non-strict equals == if you need it to be more flexible. If all of the feature ids are numeric (and none of them have leading zeros) and you have the ability to, I would change the feature ids to be actual numbers so you can keep it more intuitive by just passing in a number instead of a string representation of a number: getIdByFeatureId(3032); while keeping the strict comparison.
Related
I have a node.js application that pulls some data and sticks it into an object, like this:
var results = new Object();
User.findOne(query, function(err, u) {
results.userId = u._id;
}
When I do an if/then based on that stored ID, the comparison is never true:
if (results.userId == AnotherMongoDocument._id) {
console.log('This is never true');
}
When I do a console.log of the two id's, they match exactly:
User id: 4fc67871349bb7bf6a000002 AnotherMongoDocument id: 4fc67871349bb7bf6a000002
I am assuming this is some kind of datatype problem, but I'm not sure how to convert results.userId to a datatype that will result in the above comparison being true and my outsourced brain (aka Google) has been unable to help.
Mongoose uses the mongodb-native driver, which uses the custom ObjectID type. You can compare ObjectIDs with the .equals() method. With your example, results.userId.equals(AnotherMongoDocument._id). The ObjectID type also has a toString() method, if you wish to store a stringified version of the ObjectID in JSON format, or a cookie.
If you use ObjectID = require("mongodb").ObjectID (requires the mongodb-native library) you can check if results.userId is a valid identifier with results.userId instanceof ObjectID.
Etc.
ObjectIDs are objects so if you just compare them with == you're comparing their references. If you want to compare their values you need to use the ObjectID.equals method:
if (results.userId.equals(AnotherMongoDocument._id)) {
...
}
converting object id to string(using toString() method) will do the job.
The three possible solutions suggested here have different use cases.
Use .equals when comparing ObjectId on two mongoDocuments like this
results.userId.equals(AnotherMongoDocument._id)
Use .toString() when comparing a string representation of ObjectId to an ObjectId of a mongoDocument. like this
results.userId === AnotherMongoDocument._id.toString()
According to the above,i found three ways to solve the problem.
AnotherMongoDocument._id.toString()
JSON.stringify(AnotherMongoDocument._id)
results.userId.equals(AnotherMongoDocument._id)
The accepted answers really limit what you can do with your code. For example, you would not be able to search an array of Object Ids by using the equals method. Instead, it would make more sense to always cast to string and compare the keys.
Here's an example answer in case if you need to use indexOf() to check within an array of references for a specific id. assume query is a query you are executing, assume someModel is a mongo model for the id you are looking for, and finally assume results.idList is the field you are looking for your object id in.
query.exec(function(err,results){
var array = results.idList.map(function(v){ return v.toString(); });
var exists = array.indexOf(someModel._id.toString()) >= 0;
console.log(exists);
});
I faced exactly the same problem and i simply resolved the issue with the help of JSON.stringify() as follow:-
if (JSON.stringify(results.userId) === JSON.stringify(AnotherMongoDocument._id)) {
console.log('This is never true');
}
Mongoose from 5 to 6 migration guide:
"Mongoose now adds a valueOf() function to ObjectIds. This means you can now use == to compare an ObjectId against a string."
https://mongoosejs.com/docs/migrating_to_6.html#objectid-valueof
Here is an example that explains the issue and why it confusing for many. Only the first console log shows the object in its true form, and any other debuging/loging will be confusing because they look the same.
// Constructor for an object that has 'val' and some other stuffs
// related to to librery...
function ID(_val) {
this.val = _val;
this.otherStuff = "other stuffs goes here";
}
// function to help user get usefull infos from the Object
ID.prototype.toString = function toString() {
return `${this.val}`;
};
// Create new Object of type ID
const id = new ID('1234567');
console.log("my ID: ", id); // my ID: Object {
// val: "1234567",
// otherStuff: "other stuffs goes here"
// }
console.log("my ID: " + id); // my ID: 1234567
console.log(id === '1234567'); // false
console.log(id == '1234567'); // true
console.log(id.toString() === '1234567'); //true
console.log(`${id}` === '1234567'); // true
console.log(new ID('1234567') === id); // false
I've read the answers to this question why is typeof null "object"? and I'm trying to figure out a solution to a specific problem.
I am working on a Web application with angularJs for the front-end and ASP.NET MVC for the server code. We're passing our models to the front-end and then updated values back. Here is the situation I'm trying to find a solution for:
When we're deleting numbers from the input type="number" using a backspace key in the Web interface, the value of the input control becomes null. In our models the numeric values are defined as not nullable (and in our database the strings also should be converted to empty values instead of null).
I am trying to figure out a way to set keys to 0 if they are numeric and to empty string if they are strings.
I've added a new method called verifyInput and here is the beginning of the code:
/**
* #returns true or false
* */
Model.prototype.verifyInput = function () {
for (let i = 0; i < this.keys.length; i++) {
const key = this.keys[i];
if (this[key] === null) {
this[key] =
}
}
}
I am not sure how to write the above method. I already found out that I can not use typeof or lodash methods to check for key's type as it's returning object. I need to set numbers to 0 and strings to empty strings, but leave datetime values alone. Do you see my problem and do you have ideas of a solution? Perhaps I should use try/catch here?
The idea I had of deleting these keys seem to work. I'm getting the correct behavior (e.g. the values automatically come as 0 in this case to the server).
I added the following code:
Model.prototype.verifyInput = function () {
let i = this.keys.length;
while (i--) {
const key = this.keys[i];
if (this[key] === null) {
this.keys.splice(i, 1); // Let's remove the null element from the model
}
}
return true;
}
based on another very helpful answer I found Looping through array and removing items, without breaking for loop and in the tests I made so far it worked nicely.
Summary: Is there a faster way to hash objects than JSON.stringify?
Details: I have a Ruby and JavaScript library (NeatJSON) that provides pretty-printing of JavaScript values. I recently fixed a problem where deeply-nested objects caused O(n!) performance (n being the nesting level) using memoization based on the object being serialized and the indentation amount.
In Ruby, the fix was really easy, because you can index hashes by arrays of unique sets of objects:
build = ->(object,indent) do
memoizer[[object,indent]] ||= <all the rest of the code>
end
In JavaScript, however, I can't index an object by another object (in a unique way). Following the lead of several articles I found online, I decide to fix the problem generically, using JSON.stringify on the full set of arguments to the function to create a unique key for memoization:
function memoize(f){
var memo = {};
var slice = Array.prototype.slice;
return function(){
var args = slice.call(arguments);
var mkey = JSON.stringify(args);
if (!(mkey in memo)) memo[mkey] = f.apply(this,args);
return memo[mkey];
}
}
function rawBuild(o,indent){ .. }
var build = memoize(rawBuild);
This works, but (a) it's a little slower than I'd like, and (b) it seems wildly inefficient (and inelegant) to perform (naive) serialization of every object and value that I'm about to serialize smartly. The act of serializing a large object with many values is going to store a string and formatting result for EVERY unique value (not just leaf values) in the entire object.
Is there a modern JavaScript trick that would let me uniquely identify a value? For example, some way of accessing an internal ID, or otherwise associating complex objects with unique integers that takes O(1) time to find the identifier for a value?
If you are looking to memoise your objects by identity (not by content), then you'll want to use a WeakMap which is designed for exactly this purpose. They don't work for primitive values though, so you'll need a different solution for such arguments.
Using #Bergi's suggestion of a WeakMap I found out about Map, which allows using any value type as the key (not just objects). Because I needed a compound key—uniquely memoizing the combination of the value passed in and the indentation string—I created a hierarchical memoization structure:
function memoizedBuild(){
var memo = new Map;
return function(value,indent){
var byIndent=memo.get(value);
if (!byIndent) memo.set(value,byIndent={});
if (!byIndent[indent]) byIndent[indent] = rawBuild(value,indent);
return byIndent[indent];
}
}
This proved to be about 4× faster than the memoization code I had been using when serializing a large 270kB JSON object.
Note that in the above code I'm able to use !byIndent[indent] only because I know that rawBuild will never return a falsey value (null, undefined, false, NaN, 0, ""). The safer code line would look something like:
if (!(indent in byIndent)) byIndent[indent] = rawBuild(value,indent);
If you just need to memoise objects then it makes sense to assign some unique ID to your objects .
var gID = 0;
function createNode() {
var obj = ...
obj.id = (++gID).toString();
}
and use those obj.id's as keys in your memo collection.
That would be fastest and least greedy solution.
Update:
If you want that id property to do not clash with existing properties
then you can create non-enumerable properties using standard ES5.1 Object.createProperty() (with some unique name) or to use ES6 symbols:
var gID = 0;
var gUidSym = Symbol("uid");
function getUidOf(obj) {
return obj[gUidSym]
|| (obj[gUidSym] = (++gID).toString());
}
This is a fairly common question here in SO, and I've looked into quite a few of them before deciding to ask this question.
I have a function, hereby called CheckObjectConsistency which receives a single parameter, an object of the following syntax:
objEntry:
{
objCheck: anotherObject,
properties: [
{
//PropertyValue: (integer,string,double,whatever), //this won't work.
PropertyName: string,
ifDefined: function,
ifUndefined: function
}
,...
]
}
What this function does is... considering the given parameter is correctly designed, it gets the objCheck contained within it (var chk = objEntry.objCheck;), It then procedes to check if it contains the properties contained in this collection.
Like this
for(x=0;x<=properties.length;x++){
if(objCheck.hasOwnProperty(properties[x].PropertyName)){
properties[x].ifDefined();
}
else{
properties[x].ifUndefined();
}
What I want is... I want to bring it to yet another level of dynamicity: Given the propositions that IfDefined and IfUndefined are functions to be called, respectively, if the currently-pointed PropertyName exists, and otherwise, I want to call these functions while providing them, as parameters, the very objCheck.PropertyName's value, so that it can be treated before returning to the user.
I'll give a usage example:
I will feed this function an object I received from an external provider (say, a foreign JSON-returning-WebService) from which I know a few properties that may or may not be defined.
For example, this object can be either:
var userData1 = {
userID : 1
userName: "JoffreyBaratheon",
cargo: "King",
age: 12,
motherID : 2,
//fatherID: 5,--Not defined
Status: Alive
}
or
var userData2 = {
userID :
userName: "Gendry",
cargo: "Forger Apprentice",
//age: 35, -- Not Defined
//motherID: 4,-- Not Defined
fatherID: 3,
Status: Alive
}
My function will receive:
var objEntry=
{
objCheck: userData1,
properties: [
{
PropertyName: "age",
ifDefined: function(val){alert("He/she has an age defined, it's "+val+" !");},
ifUndefined: function(){alert("He/she does not have an age defined, so we're assuming 20.");},
},
{
PropertyName: "fatherID",
ifDefined: function(val){alert("He/she has a known father, his ID is "+val+" !");},
ifUndefined: function(){alert("Oh, phooey, we don't (blink!blink!) know who his father is!");},
}
]
}
CheckObjectConsistency(objEntry); // Will alert twice, saying that Joffrey's age is 12, and that his father is supposedly unknown.
ifDefined will only actually work if, instead of properties[x].ifDefined();, I somehow provide it with properties[x].ifDefined(PropertyValue);. And here, at last, lies my question.
Being inside the consistency-checking-function, I only know a given property's name if it's provided. Being inside it, I can't simply call it's value, since there is no such function as properties[x].ifUndefined(properties[x].GetValueFromProperty(properties[x].PropertyName)) ,... is there?
I'm sorry. Not being a native english speaker (I'm brazilian), I can't properly express my doubts in a short way, so I prefer to take my time writing a long text, in an (hopefully not wasted) attempt to make it clearer.
If, even so, my doubt is unclear, please let me know.
I think you're looking for the bracket notation here. It allows you to provide an arbitrary value as key to access the object. Also, you know its name. You have your properties object right?
objEntry.properties.forEach(function(property){
// Check if objCheck has a property with name given by PropertyName
if(!objEntry.objCheck.hasOwnProperty(property.PropertyName)){
// If it doesn't, call isUndefined
property.isUndefined();
} else {
// If it does, call isDefined and passing it the value
// Note the bracket notation, allowing us to provide an arbitrary key
// provided by a variable value to access objCheck which in this case is
// the value of PropertyName
property.isDefined(objEntry.objCheck[property.PropertyName]);
}
});
Oh yeah, forEach is a method of arrays which allows you to loop over them. You can still do the same with regular loops though.
I'm receiving data from the GMail API using $.ajax() with dataType: "xml", then throwing it into $.xml2json, which is a jQuery plugin. The problem is (as ironically demonstrated by the demo on the page) that when I put in something like this:
<animals>
<dog>
<name>Rufus</name>
<breed>labrador</breed>
</dog>
<dog>
<name>Marty</name>
<breed>whippet</breed>
</dog>
<cat name="Matilda"/>
</animals>
I get this:
{
animals: {
dog: [
{
name: 'Rufus',
breed: 'labrador'
}, {
name: 'Marty',
breed: 'whippet'
}
],
cat: {
name: 'Matilda'
}
}
}
Anyway, as you can see, in <animals> there are two <dog>s and only one <cat>; therefore, animals["cat"] is an object, while animals["dog"] is an array of objects. Likewise, when there is only one <entry> returned by GMail, feed["entry"] is an array of what is inside it, but when there is more than one tag, feed["entry"] is an array of entries, which in turn are arrays of what is inside them.
What I want to do is do different things, depending on whether I have 1 or more entries, using a conditional statement; to do that I need a boolean which tells me how many entries there are. How would I go about doing that? JQuery welcome.
EDIT: I guess another possible answer would be to edit the source of the plugin itself and make it offer some sort of way of knowing if it is a lone tag (an array-like object) or if there are lots of them (a "true" array).
I think the best thing you can do here is to simply concat the object/array to an empty array:
var obj = {a:"hello"};
[].concat(obj); // [{a:"hello"}]
var arr = [{a:"hello"}];
[].concat(arr); // identical results
So to fix your object:
for ( var prop in obj ) {
obj[prop] = [].concat(obj[prop])
}
This way you will always get an array and don't have to worry about which one you have.
var json = {
animals: {
dog: [
{
name: 'Rufus',
breed: 'labrador'
}, {
name: 'Marty',
breed: 'whippet'
}
],
cat: {
name: 'Matilda'
}
}
};
for ( var i in json ) {
if ( json.hasOwnProperty( i ) ) {
if ( json[i] instanceof Array ) {
// you have an array, do as you please
} else {
// you have an object, do as you please
}
}
}
You can use jQuery type() method to streamline your code and take advantage of fully cross browser compliant tests
API Refernce : http://api.jquery.com/jQuery.type/
$.each(data, function(i, item) {
switch($.type(item)) {
case 'string':
/* string code*/
break;
case 'array':
/* aray code*/
break;
/* ....etc*/
}
});
That is a bug in xml2json, and more generally a problem with converting XML to JSON--without a description document like XSLT or XSD, there's no way to tell the serializer that something is supposed to be a collection. You might want to look into something like this for turning XML into JSON in a more formal manner.
In the meantime, here's a way to force a variable to be an array if it isn't and leave it unchanged if it is:
x = [].concat(x);
Note: this was edited after the fact to use #Asad's superior method from his answer
This starts to fail with multidimensional arrays, as you've observed, so it sounds like you'd better start using XSLT to describe which elements are always collections.
And don't use instanceof
There is an edge case where instanceoffails. The instanceof operator used like x instanceof Array tests whether a particular constructor (in this case window.Array) is present in the prototype chain. But this fails when the variable came from another window, such as a popup window or an iframe that exists on the same domain and can pass objects directly to its parent.