Suppose I have an object that looks like this:
var SomeObject = {
'6.2013' : SomeArray1,
'7.2013' : SomeArray2,
'8.2013' : SomeArray3,
'9.2013' : SomeArray4
};
Then, somewhere in my code, I have a global variable that I set like this:
CurrentObject = SomeObject['7.2013'];
Now somewhere else in my code, I need to know the key that CurrentObject is pointing to. Basically, I need to do this:
var SomeObjectKey = .....(CurrentObject);
and that be the string '7.2013'.
Thanks for your suggestions.
found = Object.keys(SomeObject).filter(function(key) {
return SomeObject[key] === CurrentObject
})
returns a list of keys that "point" to the value (there can be multiple ones).
As pointed out in the comments, that doesn't work in IE8 and also in Netscape navigator, Mosaic 1.2.3 and probably Lynx for DOS as well, in case anyone gives a ...
If you desperately need to support outdated engines, don't cripple your code, keep it modern, nice and clean and use shims to patch the broken parts.
The simple cross browser way would be to iterate :
var SomeObject = {
'6.2013' : 'SomeArray1',
'7.2013' : 'SomeArray2',
'8.2013' : 'SomeArray3',
'9.2013' : 'SomeArray4'
};
var CurrentObject = SomeObject['7.2013'],
SomeObjectKey;
for (key in SomeObject) {
if (SomeObject[key] == CurrentObject) SomeObjectKey = key;
}
console.log(SomeObjectKey); // returns 7.2013
FIDDLE
There's no "standard" call to do this, you would have to iterate over all keys of the object until you find one whose value matches the desired value.
Note that if your values are "object type" (i.e. including arrays) then a straight === (or ==) test will only compare for reference equality (i.e. that the two arrays are actually the exact same two arrays) and not that their contents are equal.
Based somewhat on thg435's answer, you could add this functionality:
Object.keysFor(obj, value) = function() {
return Object.keys(obj).filter(function(key) {
return obj[key] === value;
}
}
with usage:
var SomeObjectKey = Object.keysFor(SomeObject, CurrentObject);
For older browsers, shims for Object.keys and Array.prototype.filter are available at the MDN website.
Related
I have some code structured like this but with a bunch of variables with paths of various depths within dict that may or may not exist:
var dict = {
'test1': 'test',
'test2': ['testa', 'testb'],
}
var test_path1 = dict['test2']['testa'] ? test_path1: false
var test_path2 = dict['test3']['testz'] ? test_path2: false
console.log(test_path2)
Basically my program creates a bunch of arrays that it saves within user_dict depending on user input. Later I need to process dict and check some of the variables to see their values, or whether or not they exist.
I can't even get to that point though, since defining test_path2 returns "cannot read property testz of undefined."
I thought using ? test_path2: false would work, but I still get that same error.
Someone suggested using optional chaining, but that doesn't seem like a good solution since some of my variables are located within 4-5 nested objects/arrays, each of which may or may not exist.
What's the best way to handle this? Is there an error with my syntax or am I approaching the problem the wrong way? All I need is for test_path1 and test_path2 to return false if it doesn't exist.
Arguably, the best way to handle this (while keeping legacy compatibility) is using get from lodash:
import { get } from 'lodash';
const result = get(dict, ['test2', 'testa']) || false;
// or
const result = get(dict, 'test2.testa') || false;
Note: to only import get (and nothing else) from lodash, use lodash-es instead of lodash. In other words, by using lodash-es you enable tree-shaking lodash at build step.
or you could simply check each level:
const result = dict && dict.test2 && dict.test2.testa || false;
If legacy compatibility is not an issue, you could use optional chaining, as suggested in Noriller's answer.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining
optional chaining can and will work even on deep nested objects
to top it off, you can use the nullish coalescing to return a "default" (fale in your case)
var test_path1 = dict['test2']?.['testa'] ?? false
this will return the value or false if it is undefined
You can use some modern javascript features. Try Optional Chaining:
var dict = {
'test1': 'test',
'test2': { 'testa': 'testb' },
}
var test_path1 = dict['test2']?.['testa'] || false
var test_path2 = dict['test3']?.['testz'] || false
console.log(test_path1, test_path2)
If you want to write more optimistic code you could also use a try catch at some point in the code.
try {
return dict.test1.test2
} catch (e) {
if (e instanceof TypeError)
return false
throw e
}
This prevents the need for writing code that does null checks.
JavaScript for browser
I need to test that one deeply embedded property is not null and if this condition is true, then to change its property. But any its parent can be null also. Therefore I am to check each item in the chain... Therefore I write such ugly code:
if(window[rootNamespace].vm.tech &&
window[rootNamespace].vm.tech.currentWorkType &&
window[rootNamespace].vm.tech.currentWorkType.currentVariant &&
window[rootNamespace].vm.tech.currentWorkType.currentVariant.currentVersion &&
window[rootNamespace].vm.tech.currentWorkType.currentVariant.currentVersion.currentWorkSection &&
window[rootNamespace].vm.tech.currentWorkType.currentVariant.currentVersion.currentWorkSection.currentStageSet){
window[rootNamespace].vm.tech.currentWorkType.currentVariant.currentVersion
.currentWorkSection.currentStageSet.selectedEntity = item;
}
Is there a shorter method of checking?
(I was the original poster proposing the try-catch method, but based on the discussion on that post you were worried about performance. Here's an alternate approach.)
You can use prototype methods to implement a safe method of accessing subproperties. Here is a method which can safely test for the existence of a nested property:
// Indicates whether an object has the indicated nested subproperty, which may be specified with chained dot notation
// or as separate string arguments.
Object.prototype.hasSubproperty = function() {
if (arguments.length == 0 || typeof(arguments[0]) != 'string') return false;
var properties = arguments[0].indexOf('.') > -1 ? arguments[0].split('.') : arguments;
var current = this;
for(var x = 0; x < properties.length; x++) {
current = current[properties[x]];
if ((typeof current) == 'undefined') return false;
}
return true;
};
A full set of methods can be found here, with sample code.
Timings can be run here, and indicate that using the try-catch method may run a couple of orders of magnitude slower than your original approach when errors are thrown, but is otherwise quite fast. The prototype methods are more generic and can lead to a more declarative style, while offering much better performance than try-catch misses, but obviously not quite as good as hand-crafting if statements each time and/or try-catch without a miss.
I've also blogged about this approach.
Syntax wise I don't think so, but I recommend refactoring at least.
var getCurrentStageSet = function(window){
return window[rootNamespace].vm.tech &&
window[rootNamespace].vm.tech.currentWorkType &&
window[rootNamespace].vm.tech.currentWorkType.currentVariant &&
window[rootNamespace].vm.tech.currentWorkType.currentVariant.currentVersion &&
window[rootNamespace].vm.tech.currentWorkType.currentVariant.currentVersion.currentWorkSection &&
window[rootNamespace].vm.tech.currentWorkType.currentVariant.currentVersion.currentWorkSection.currentStageSet
}
var setSelectedEntity = function(currentStageSet, item){
currentStageSet.selectedEntity = item;
}
By abstracting this logic your actual set of the property will be more readable, and reusable:
var currentStageSet = getCurrentStageSet(window);
if (currentStageSet){
setSelectedEntity(currentStageSet, item);
}
For such a trivial, self-contained piece of code, it's probably not unreasonable to just catch and ignore the error (possibly log) e.g.
try {
if (window[rootNamespace].vm.tech.currentWorkType.currentVariant.currentVersion.currentWorkSection.currentStageSet) {
window[rootNamespace].vm.tech.currentWorkType.currentVariant.currentVersion.currentWorkSection.currentStageSet = item;
}
} catch (e) {
// log the error but continue
}
Not sure what else could really go wrong in this type of check, alternatively you could catch a TypeError specifically but not sure it would really matter all that much.
I generally wouldn't recommend catch all's but in this case it seems self contained enough to not be a huge risk.
Anything beyond that requires effort e.g. building an object decorator or a fluent interface type solution, seems overkill to me though.
You can create some variables to get code more readable
var tech = window[rootNamespace].vm.tech;
var workType, curVariant, curVer, curWorkSection;
if(tech){
workType = tech.currentWorkType
}
if(workType){
curVariant = workType.currentVariant;
}
if(curVariant){
curVer =curVariant.currentVersion;
}
if(curVer){
curWorkSection = curVer.currentWorkSection;
}
if(curWorkSection && curWorkSection.currentStageSet){
curWorkSection.currentStageSet.selectedEntity = item;
}
This is the most compact syntax possible in basic JavaScript. It avoids all the null-checking by using error-trapping instead. None of the other answers are as compact because the language is simply missing the feature you're after from C#.
Apparently, I'm being down-voted by the authors of the other, much less compact answers, but this is nevertheless the only single-line answer. Note that other approaches listed here have you creating multiple functions, even. :| If you want compact, this is it.
try { window[rootNamespace].vm.tech.currentWorkType.currentVariant.currentVersion
.currentWorkSection.currentStageSet.selectedEntity = item; } catch (err) {}
I know I could use variations of this answer to find out how many different types of data there are in an array:
How to count the number of certain element in an array?
But, what I am looking for is whether there is a simple way to count how many different types of data there are in an array:
I have arrays which may have 0, 1, or 2 as values
so it could be:
a = [1,2,0,1,1,1];
or they may all be the same and a different length:
a = [1,1,1,1,1,1,1,1,1,1,1,1,1];
In javascript, I would like a function that returns "1" if all values are the same, "2" if there is a mixture of just two of the possible values, and if the array contains all three values, "3".
Any help appreciated. Thanks.
The simple approach is to keep a map of found values, and remember every time you add to it: Live Example | Live Source
function countUniques(a) {
var valuesSeen = {};
var count = 0;
a.forEach(function(value) {
if (!valuesSeen[value]) {
++count;
valuesSeen[value] = true;
}
});
return count;
}
(Note: That uses ES5's forEach. If you're using an old browser, you'll need to shim that, search for "ES5 shim" to find a shim for it.)
Or if you don't like the people you work with very much and love using operators instead of branching statements: Live Copy | Live Source
function countUniques(a) {
var valuesSeen = {}, count = 0;
a.forEach(function(value) {
valuesSeen[value] || (++count, valuesSeen[value] = true);
});
return count;
}
Just for fun, here's a "funkier" (and somewhat obfuscated) solution using .reduce that requires no local variables:
function countUniques(a) {
return a.reduce(function(p, v) {
p.c += !(v in p.s); p.s[v] = 1; return p;
}, {c:0, s:{}}).c;
}
It's functionally identical to TJC's answer, except that the valuesSeen and count values are passed around as an object p as the "previous" value passed from the prior iteration of .reduce. The p.c element is equivalent to TJC's count and p.s is valuesSeen.
Note that .reduce (like .forEach) is an ES5 function which will require a shim on older browsers.
I'm trying to find the most efficient way of deleting properties from an object whose properties of commentCount and likeCount are both equal to 0. In the following example, Activity.3 would be removed. I don't want loop over them with a $.each() as that seems like it would take more time than necessary.
Activity = {
0 : {
'commentCount' : 10,
'likeCount' : 20
},
1 : {
'commentCount' : 0,
'likeCount' : 20
},
2 : {
'commentCount' : 10,
'likeCount' : 0
},
3 : {
'commentCount' : 0,
'likeCount' : 0
}
}
UPDATE
The circumstances of the creation of this object have come into question. To clarify, the Activity object can have up to 3 million properties inside of it. It's generated server side as an AJAX JSON response which is saved into memory. It includes more than just commentCount and likeCount that are used elsewhere, so I can't just not have the server not respond with things that have a 0 for both commentCount and likeCount.
Ah, the smell of premature optimization ^_^
How many of these objects do you have? How many do you need to clean them? If the answers are "less than 1 million" and "once or rarely", it's probably not worth to bother.
If you need a quick and optimal way, here is an idea: Create a new data structure and setters for the properties. Every time they are set, check whether they are both 0 and put them into a "kill" list.
That way, you just have to iterate over the kill list.
[EDIT] With several million objects and the need for a quick cleanup, a kill list is the way to go, especially when the condition is rare (just a few objects match).
Just write a function that updates these properties and make sure all code goes through it to update them. Then, you can manage the kill list in there.
Or you can simply delete the object as soon as the function is called to set both or the second property to 0.
I'm adding a second answer, because this solution comes from a completely different angle. In this solution, I attempt to find the fastest way to remove unwanted entries. I'm not aware of any way of doing this without loops, but I can think of several ways to do it with loops using jQuery as well as just raw javascript.
This jsperf shows all of the test cases side-by-side.
I'll explain each test and the caveats associated with each.
Raw JS: Slowest option. It looks like jQuery knows what they are doing with their $.each and $.map loops.
var obj;
for (var field in Activity) {
if (Activity.hasOwnProperty(field)) {
obj = Activity[field];
if (obj.commentCount === 0 && obj.likeCount === 0) {
delete Activity[field];
}
}
}
$.each: Tied for 2nd place. Cleaner syntax and faster than raw js loop above.
$.each(Activity, function(key, val){
if (val.commentCount === 0 && val.likeCount === 0) {
delete Activity[key];
}
});
$.map (Object version): Tied for 2nd place. Caveat: only supported in jQuery >= 1.6.
Activity = $.map(Activity, function(val, key){
if (val.commentCount === 0 && val.likeCount === 0) {
return null;
}
});
$.map (Array version): Fastest option. Caveat: You must use the $.makeArray function to convert your object to an array. I'm not sure if this is suitable for your needs.
var arrActivity = $.makeArray(Activity);
Activity = $.map(arrActivity, function(val, key){
if (val.commentCount === 0 && val.likeCount === 0) {
return null;
}
});
Conclusion
It looks like $.map is the fastest if you convert your object to an array using $.makeArray first.
This is just a starting-point, but how about something like this? Basically, it puts the Activities into buckets based on the sum of their likeCount and commentCount. It makes it easy to kill all of the Activities with no likes or comments, but I would assume there is a trade-off. I'm not sure how you are inserting these things and reading them. So, you'll have to decide if this is worth it.
var ActivityMgr = function(){
if(!(this instanceof ActivityMgr)){
return new ActivityMgr();
}
this.activities = {};
};
ActivityMgr.prototype.add = function(activity){
var bucket = parseInt(activity.commentCount, 10) + parseInt(activity.likeCount, 10);
if (this.activities[bucket] === undefined) {
this.activities[bucket] = [activity];
}
else {
this.activities[bucket].push(activity);
}
this.cleanse();
};
ActivityMgr.prototype.cleanse = function(){
this.activities[0] = [];
};
//Usage:
var activityMgr = new ActivityMgr();
activityMgr.add({
likeCount: 0,
commentCount: 10
});
EDIT:
After posting this, it becomes incredibly apparent that if you are adding items in this manner, you could just not add them if they have no likes or comments. My guess is that things aren't that simple, so please provide some detail as to how things are added and updated.
Ok, I know this is a stupid question, and stupid quesitons are most commonly the hardest to find answers for as everyone assumes they are just known. Anyway, i have this:
var map = {
"key1" : "someValue1",
"key2" : "someValue2"
};
And then I enumerate the entries thusly:
Y.Object.each( map, function(value,key,object) {
// do something;
} );
But what I really want to do is something like:
map = Y.Object.wrap(map);
map.each( function(value,key,object) {
} );
It is worth noting that the original object may be passed in as an argument, so I will not always be constructing them. In other words, i really do want to wrap one (and in a way that will not double wrap if it is already wrapped). The answer is probably obvious and even in the documentation, I just cant find it. So I through myself at the mercy of the googlesphere...
--------------------- EDIT --------------------
When I say wrap, I guess what I mean is (in YUI speak) augment. In other words, I would like the Y.Object methods directly available on the object in question. For example, I want to be able to do something like this:
var map = {
"key1" : "someValue1",
"key2" : "someValue2"
};
map.each( function(value,key,object) {
//do something with each entry in my map
} );
Instead of having to always do this:
var map = {
"key1" : "someValue1",
"key2" : "someValue2"
};
Y.Object.each( map, function(value,key,object) {
//do something with each entry in my map
} );
There are two reasons for this. First, I am lazy and don't want to have to keep typing Y.Object.each( myObject... when all I want is to iterate through each property of my object. Second, I want to be forward compatible so that when the functions each and some get defined natively (which I think they already are in firefox and chrome) I can leverage the native implementations without code change. I think the second reason is something Crockford seems to emphasize since he always seems to say that if you want to add a function to the prototype of an Object, do so in a way that will check to see if the function is already defined before doing so. Feel free to correct me if I am wrong anywhere above, and as always, feel free to suggest a solution :)
As best I know, YUI3 doesn't have a wrap function that works for regular objects and adds your desired each() method for them. But, you could make your own like this:
YUI().use('node', function(Y) {
// code to declare our new wrapper function
function myWrap(o) {
if (o instanceof myIterator) return(o); // don't double wrap
return(new myIterator(o));
}
// code to declare a base class used in the iterator
function myIterator(o) {
Y.mix(this, o);
}
// the actual each() iteration function
myIterator.prototype.each = function(fn) {
var key;
for (key in this) {
if (this.hasOwnProperty(key)) {
fn.call(this, this[key], key, this);
}
}
}
// start of regular code that uses the new wrapper
var map = {
"key1" : "someValue1",
"key2" : "someValue2"
};
var x = myWrap(map);
x.each(function(value, key, object) {
console.log(value);
});
});
You can see it work here: http://jsfiddle.net/jfriend00/h7Fpp/.