In Javascript, given value, find name from Object literal - javascript

I'm new JavaScript and trying to find out an easier way to find name given a value from object literal.
e.g.
var cars ={ Toyata: ['Camry','Prius','Highlander'],
Honda: ['Accord', 'Civic', 'Pilot'],
Nissan: ['Altima', 'Sentra', 'Quest']};
Given 'Accord', I want to get Honda from the object Cars.

You would need to loop through, like this:
function getManufacturer(carName) {
for(var key in cars) {
if(cars.hasOwnProperty(key)) {
for(var i=0; i<cars[key].length; i++) {
if(cars[key][i] == carName) return key;
}
}
}
return "Not found";
}
You can test it out here, for the same of working cross-browser, this ignores the existence of .indexOf() since IE doesn't have it...that version would look like this:
function getManufacturer(carName) {
for(var key in cars) {
if(cars.hasOwnProperty(key) && cars[key].indexOf(carName) != -1) {
return key;
}
}
return "Not found";
}

If you're going to be doing this once, then use a function like the one given by Bobby. If you're going to be doing this multiple times then I'd suggest creating a reverse mapping of cars to manufacturers:
var manufacturers = {};
// create a map of car models to manufacturers:
for (var manf in cars) {
/* see note below */
for (var i=0; i<cars[manf].length; i++) {
manufacturers[cars[manf][i]] = manf;
}
}
// Now referencing the manufacturers is
// a very fast hash table lookup away:
var model = 'Accord';
alert(manufacturers[model]);
note for those with itchy downvoting fingers: For objects that don't inherit anything as given in the OP a hasOwnProperty check here is unnecessary. For objects that do inherit it depends on the programmer. If you want composability via inheritance then a hasOwnProperty check is exactly what you DONT want. If you don't care about inheritance then use a hasOwnProperty check but if so you would not be inheriting in the first place which would make a hasOwnProperty check unnecessary. In the rare case where you are forced to create the object via inheritance but don't want to check the parent's attributes then you should do a hasOwnProperty check. Of course, if you use a library like Prototype.js that insists on modifying the Object object then I feel sorry for you because you are forced to do a hasOwnProperty check.

Maintain a separate mapping of models to manufacturers.
var cars ={ Toyata: ['Camry','Prius','Highlander'],
Honda: ['Accord', 'Civic', 'Pilot'],
Nissan: ['Altima', 'Sentra', 'Quest']};
var models = {};
var hasOwnProperty = Object.prototype.hasOwnProperty;
for (key in cars) {
if (hasOwnProperty.call(cars, key)) {
var i=0,l=cars[key].length,manufacturer=cars[key];
while (i<l) {
if ( ! hasOwnProperty.call(models, manufacturer)) {
models[manufacturer] = key;
} else {
// Throw an error, or change the value to an array of values
}
i++;
}
}
}

Related

Concatenate multiple object property values into one

If I have object with following structure:
var test = {
property1: "value1",
property2: "value2",
property3: "value3",
property4: "value4",
property5: "value5"
}
Assuming that property names are fixed and not always in this order, what is the most elegant way to convert this object into following one:
var test_copy = {
prop1Copy: "value1",
propConcat: "value2, value3, value4, value5"
}
I don't think there's any particularly elegant way to do this.
Since your input data has a small number fixed keys there's barely any point using a loop, so this works:
function munge(o) {
return {
prop1Copy: o.property1,
propConcat: [o.property2, o.property3, o.property4, o.property5].join(', ')
}
}
Try this:
function concatObject(object, levels){
var currentLevel = 0;
var newObj = {propConcat: ""};
for(var prop in object){
if(currentLevel < levels){
newObj[prop] = object[prop];
}
else{
newObj["propConcat"] += object[prop];
}
}
}
concatObject(test, 1) would give you the answer, however it would keep the same property name for the variables. You need some kind of function of mapping if you want to change the actual property names (example: from property1 to prop1copy)
This would transform property# to property#copy:
function concatObject(object, levels){
var currentLevel = 0;
var newObj = {propConcat: ""};
for(var prop in object){
if(currentLevel < levels){
newObj[prop+"copy"] = object[prop];
}
else{
newObj["propConcat"] += object[prop];
}
}
}
Im not sure what you need to accomplish here. But if you want copy first item and concat all other take a look at this.
function concatValues (obj) {
var resObj = {prop1Copy: ""}, count = 0, mergedArr = [];
for (var k in obj) {
count == 0 ? resObj.prop1Copy = obj[k] : mergedArr.push(obj[k]);
count++;
}
resObj.propConcat = mergedArr.join(", ");
return resObj;
}
Hope this helps
Here is a more generic solution that would work on a wider range of input with some caveats.
function concatenateObjectValues(obj) {
//If you want the output to be sorted differently, you need to provide your own sort order. This sorts by alphabetical order
var keys = Object.keys(test).sort();
//assuming the first property would always be the copy
//removing the first element and returning it
var copyProp = keys.unshift();
//generate an array that has the values of the remaining properties from the input
var concatProp = keys.reduce(function(memo, key) {
memo.push(test[key]);
return memo;
}, []);
//create `propConcat` and combine the values using the specified separator
var newObj = {
propConcat: concatProp.join(", ")
};
//add the `prop1Copy` property. The first part of the name would be derived from the actual first property .
newObj[copyProp + "Copy"] = obj[copyProp];
return newObj;
}
Assuming you want your concatenated properties in alphabetical order,
the above would work. If not, then you would need to specify a
different sort order. This can be passed in as an argument, if it's going to vary).
if the copy property is going to vary, then this code might also need to change. Also, something that can be passed in as a parameter - trivial if it's just the index, but if you have to look them up by name (e.g., if you want to say "prop1" regardless of where it is., you need to also implement that).
if the names propConcat and prop1Copy need to vary more than that, the logic needs to be implemented. Or the values passed in...
there is no validation. I kept it simple for the sake of the example, but some error handling would be good.
To be honest, if your expected output is going to vary by more than one thing, for example, if you need the copy property to be different and the sort order to be different, then it might just be better to scrap this function. Big variations in the expected input/output make it a bit unwieldy, if you need to pass in most of the stuff to construct the result.

Adding and Removing Values from JavaScript Array

I have a JavaScript array of numbers. My array is defined like this:
var customerIds = [];
I have a function that is responsible for inserting and removing ids to/from this array. Basically, my function looks like this:
function addOrRemove(shouldAdd, customerId) {
if (shouldAdd) {
if (customerIds.contains(customerId) === false) {
customerIds.push(customerId);
}
} else {
customerIds.remove(customerId);
}
}
This function is basically pseudocode. A JavaScript array does not have a contains or remove function. My question is, is there any elegant way of tackling this problem? The best I can come up with is always looping through the array myself and tracking the index of the first item found.
Thank you for any insights you can provide.
The contains can be achieved with Array.prototype.indexOf, like this
if (customerIds.indexOf(customerId) === -1) {
indexOf function returns -1, if it couldn't find the parameter in the array, otherwise the first index of the match. So, if the result is -1, it means that customerIds doesn't contain customerId.
The remove can be achieved with Array.prototype.indexOf and Array.prototype.splice, like this
var index = customerIds.indexOf(customerId);
if (index !== -1) {
customerIds.splice(index, 1);
}
Similarly, indexOf function returns -1, if it couldn't find the parameter in the array, otherwise the first index of the match. So, if the result is -1, we skip deleteing, otherwise splice 1 element starting from the position index.
You can extend the Array method like below after that you are free to use 'contains' and 'remove'
if (!Array.contains)
Array.prototype.contains = function(a) {
for (var i in this) {
if (this[i] == a) return true;
}
return false
}
if (!Array.remove)
Array.prototype.remove = function(a) {
for (var i in this) {
if (this[i] == a) {
this.splice(i, 1);
}
}
}
Use indexOf and splice
function addOrRemove(shouldAdd, customerId) {
if (shouldAdd) {
if (customerIds.indexOf(customerId) == -1) {
customerIds.push(customerId);
}
} else {
var index = customerIds.indexOf(customerId)
customerIds.splice(index, 1);
}
}
You could definitely use the splice and indexOf as stated by #thefourtheye, yet I would like to provide another approach.
Instead of using an array you could use an object.
var customerIds = {};
//This could also be stated as: var customerIds = new Object(); this is just shorthand
function addOrRemove(shouldAdd, customerId)
{
if(shouldAd)
{
if(!customerIds[customerId])
{
customerIds[customerId] = new Object();
customerIds[customerId].enabled = true;
}
}
else
{
if(customerIds[customerId])
{
customerIds[customerId].enabled = false;
}
}
}
You now can query against the customerIds object for a specific customerId
if(customerIds[customerId].enabled)
Using this method not only provides you with the capability of attaching multiple attributes to a given customerId, but also allows you to keep records of all customerIds after disabling (removing).
Unfortunately, in order to truely remove the customerId, you would need to loop through the object and append each property of the object to a new object except for the one you do not want. The function would look like this:
function removeId(customerId)
{
var n_customerIds = new Object();
for(var key in customerIds)
{
if(key != customerId)
{
n_customerIds[key] = customerIds[key];
}
}
customerIds = n_customerIds;
}
In no way am I stating that this would be the proper approach for your implementation, but I am just providing another method of achieving your goal. There are many equivalent ways to solve your dilemma, and it is solely decided by you which method will best suit your projects functionality. I have personally used this method in many projects, as well as I have used the methods posted by others in many other projects. Each method has their pros and cons.
If you do wish to use this method, I would only suggest doing so if you are not collecting many customerIds and do want a lot of customerData per each customerId, or, if you are collecting many customerIds and do not want a lot of customerData per each customerId. If you store a lot of customerData for a lot of customerIds, you will consume a very large amount of memory.

Referencing index of an Object within an Object using something equivelant Object.indexOf

I'm surprised that I can't find an answer to this question on StackOverflow (maybe I'm not searching right).
But basically I'm curious to know if there is something similar to the Array.indexOf() method, but for objects. That is, an efficient method of returning the index(es) of a value within an existing Object.
For example, say I have an object:
var obj = { prop1: "a", prop2: "b", prop3: "c", prop4: "a" };
Now I want to find the index(es) that contain "a", it would be nice to do a obj.indexOf("a") and have it return something like ["prop1", "prop4"]
But this doesn't seem to be an implemented method for objects.
Alternatively, I know I can create a function:
function indexOf(val, obj){
var indexes = [];
for (var index in obj){
if(!obj.hasOwnProperty(index)) continue;
if(obj[index] == val){
indexes.push(index);
}
}
if(!indexes.length) return false;
else return indexes;
}
indexOf("a", obj); // returns ["prop1","prop4"]
But this kind of feels clunky to iterate over the whole object this way!! Some of the objects I'll be dealing with will be quite huge and the values might be quite large as well.
Is there a better, more efficient way?
If you have a really huge object, you could use a nice weakmap implementation with the complexity of O(1) to store keys per single object. Therefor you have to implement your hash collection, so when setting a key-value pair, you also store the key in the weakmap.
I made also some bench. comparison of this custom HashMap vs RawObject search - jsperf
function HashMap() {
this.__map = new WeakMap;
this.__hash = {};
}
HashMap.prototype = {
set: function(key, value){
this.unset(key);
if (value == null)
return;
this.__hash[key] = value;
var keys = this.__map.get(value);
if (keys == null)
this.__map.set(value, keys = []);
keys.push(key);
},
unset: function(key){
var value = this.__hash[key];
if (value) {
var keys = this.__map.get(value),
index = keys.indexOf(key);
keys.splice(index, 1);
}
this.__hash[key] = void 0;
},
get: function(key){
return this.__hash[key];
},
getKeys: function(value){
return this.__map.get(value);
}
};
WeakMap uses Object.defineProperty method in its core. For this reason there are some limitations:
browsers: IE9+
Objects as Values in above HashMap example, because they are used as Keys in WeakMap Collection
But this approach makes a huge performance boost, as there is no need to iterate over the object, to look for a specific value.

Checking for duplicate Javascript objects

TL;DR version: I want to avoid adding duplicate Javascript objects to an array of similar objects, some of which might be really big. What's the best approach?
I have an application where I'm loading large amounts of JSON data into a Javascript data structure. While it's a bit more complex than this, assume that I'm loading JSON into an array of Javascript objects from a server through a series of AJAX requests, something like:
var myObjects = [];
function processObject(o) {
myObjects.push(o);
}
for (var x=0; x<1000; x++) {
$.getJSON('/new_object.json', processObject);
}
To complicate matters, the JSON:
is in an unknown schema
is of arbitrary length (probably not enormous, but could be in the 100-200 kb range)
might contain duplicates across different requests
My initial thought is to have an additional object to store a hash of each object (via JSON.stringify?) and check against it on each load, like this:
var myHashMap = {};
function processObject(o) {
var hash = JSON.stringify(o);
// is it in the hashmap?
if (!(myHashMap[hash])) {
myObjects.push(o);
// set the hashmap key for future checks
myHashMap[hash] = true;
}
// else ignore this object
}
but I'm worried about having property names in myHashMap that might be 200 kb in length. So my questions are:
Is there a better approach for this problem than the hashmap idea?
If not, is there a better way to make a hash function for a JSON object of arbitrary length and schema than JSON.stringify?
What are the possible issues with super-long property names in an object?
I'd suggest you create an MD5 hash of the JSON.stringify(o) and store that in your hashmap with a reference to your stored object as the data for the hash. And to make sure that there are no object key order differences in the JSON.stringify(), you have to create a copy of the object that orders the keys.
Then, when each new object comes in, you check it against the hash map. If you find a match in the hash map, then you compare the incoming object with the actual object that you've stored to see if they are truly duplicates (since there can be MD5 hash collisions). That way, you have a manageable hash table (with only MD5 hashes in it).
Here's code to create a canonical string representation of an object (including nested objects or objects within arrays) that handles object keys that might be in a different order if you just called JSON.stringify().
// Code to do a canonical JSON.stringify() that puts object properties
// in a consistent order
// Does not allow circular references (child containing reference to parent)
JSON.stringifyCanonical = function(obj) {
// compatible with either browser or node.js
var Set = typeof window === "object" ? window.Set : global.Set;
// poor man's Set polyfill
if (typeof Set !== "function") {
Set = function(s) {
if (s) {
this.data = s.data.slice();
} else {
this.data = [];
}
};
Set.prototype = {
add: function(item) {
this.data.push(item);
},
has: function(item) {
return this.data.indexOf(item) !== -1;
}
};
}
function orderKeys(obj, parents) {
if (typeof obj !== "object") {
throw new Error("orderKeys() expects object type");
}
var set = new Set(parents);
if (set.has(obj)) {
throw new Error("circular object in stringifyCanonical()");
}
set.add(obj);
var tempObj, item, i;
if (Array.isArray(obj)) {
// no need to re-order an array
// but need to check it for embedded objects that need to be ordered
tempObj = [];
for (i = 0; i < obj.length; i++) {
item = obj[i];
if (typeof item === "object") {
tempObj[i] = orderKeys(item, set);
} else {
tempObj[i] = item;
}
}
} else {
tempObj = {};
// get keys, sort them and build new object
Object.keys(obj).sort().forEach(function(item) {
if (typeof obj[item] === "object") {
tempObj[item] = orderKeys(obj[item], set);
} else {
tempObj[item] = obj[item];
}
});
}
return tempObj;
}
return JSON.stringify(orderKeys(obj));
}
And, the algorithm
var myHashMap = {};
function processObject(o) {
var stringifiedCandidate = JSON.stringifyCanonical(o);
var hash = CreateMD5(stringifiedCandidate);
var list = [], found = false;
// is it in the hashmap?
if (!myHashMap[hash] {
// not in the hash table, so it's a unique object
myObjects.push(o);
list.push(myObjects.length - 1); // put a reference to the object with this hash value in the list
myHashMap[hash] = list; // store the list in the hash table for future comparisons
} else {
// the hash does exist in the hash table, check for an exact object match to see if it's really a duplicate
list = myHashMap[hash]; // get the list of other object indexes with this hash value
// loop through the list
for (var i = 0; i < list.length; i++) {
if (stringifiedCandidate === JSON.stringifyCanonical(myObjects[list[i]])) {
found = true; // found an exact object match
break;
}
}
// if not found, it's not an exact duplicate, even though there was a hash match
if (!found) {
myObjects.push(o);
myHashMap[hash].push(myObjects.length - 1);
}
}
}
Test case for jsonStringifyCanonical() is here: https://jsfiddle.net/jfriend00/zfrtpqcL/
Maybe. For example if You know what kind object goes by You could write better indexing and searching system than JS objects' keys. But You could only do that with JavaScript and object keys are written in C...
Must Your hashing be lossless or not? If can than try to lose compression (MD5). I guessing You will lose some speed and gain some memory. By the way, do JSON.stringify(o) guarantees same key ordering. Because {foo: 1, bar: 2} and {bar: 2, foo: 1} is equal as objects, but not as strings.
Cost memory
One possible optimization:
Instead of using getJSON use $.get and pass "text" as dataType param. Than You can use result as Your hash and convert to object afterwards.
Actually by writing last sentence I though about another solution:
Collect all results with $.get into array
Sort it with buildin (c speed) Array.sort
Now You can easily spot and remove duplicates with one for
Again different JSON strings can make same JavaScript object.

JavaScript: Get first and only property name of object

If I want to enumerate the properties of an object and want to ignore prototypes, I would use:
var instance = { ... };
for (var prop in instance) {
if (instance.hasOwnProperty(prop)) {
...
}
}
What if instance only has one property, and I want to get that property name? Is there an easier way than doing this:
var instance = { id: "foobar" };
var singleMember = (function() {
for (var prop in instance) {
if (instance.hasOwnProperty(prop)) {
return prop;
}
}
})();
Maybe Object.keys can work for you. If its length returns 1, you can use yourObject[Object.keys[0]] to get the only property of the object. The MDN-link also shows a custom function for use in environments without the keys method1. Code like this:
var obj = {foo:'bar'},
kyz = Object.keys(obj);
if (kyz.length === 1){
alert(obj[kyz[0]]); //=> 'bar'
} else {
/* loop through obj */
}
1 Some older browsers don't support Object.keys. The MDN link supplies code to to make it work in these browsers too. See header Compatibility in the aforementioned MDN page
Shortest form:
instance[Object.keys(instance)[0]];
ES6+ function:
let first = v => v[Object.keys(v)[0]];
Use the function:
first({a:'first', b:'second'}) // return 'first'
var foo = {bar: 1};
console.log(Object.keys(foo).toString());
which will print the string
"bar"
Though my answer is downvoted, it's still worth to know that there is no such thing as order of keys in javascript object. Therefore, in theory, any code build on iterating values can be inconsistent. One approach could be creating an object and to define setter which actually provides counting, ordering and so on, and provide some methods to access this fields. This could be done in modern browsers.
So, to answer you question, in general you approach is still most closs-browser. You can iterate using lodash or any other modern framework wich will hide "hasOwnProperty" complexity from you. As of August'15 Object.keys can be accepted as cross-browser and universal. After all IE8 happened years ago. Still there are some cases when you just don't wont store all set of keys in array. But I'd go with Object.keys - it's more flexible compared to iteration.
Unfortunately, there is no, "list properties" function built in, and there certainly isn't a "getFirstProperty" (especially since there is no guarantee that any property will consistently be "first").
I think you're better off writing a function like this one:
/**
* A means to get all of the keys of a JSON-style object.
* #param obj The object to iterate
* #param count maximum length of returned list (defaults to Infinity).
*/
function getProperties( obj, count )
{
if( isNaN( count ) ) count = Infinity
var keys = []
for( var it in obj )
{
if( keys.length > count ) break;
keys.push( it );
}
return keys;
}
Then, you could access the name though:
instance = {"foo":"bar"}
// String() on an array of < 2 length returns the first value as a string
// or "" if there are no values.
var prop = String(getProperties(instance, 1));
This is an old post, but I ended up writing the following helper function based on Object.keys().
It returns the key and value of the first property.
getFirstPropertyKeyAndValue(sourceObject) {
var result = null;
var ownProperties = Object.keys(sourceObject);
if (ownProperties.length > 0) {
if (ownProperties.length > 1) {
console.warn('Getting first property of an object containing more than 1 own property may result in unexpected results. Ordering is not ensured.', sourceObject);
}
var firstPropertyName = ownProperties[0];
result = {key: firstPropertyName, value: sourceObject[firstPropertyName]};
}
return result;
}
Answers in here all good, and with the caveat that the order may be unreliable (although in practice it seems the order the properties are set tends to stay that way), this quick and dirty method also works:
var obj = {foo: 1, bar: 2};
for(var key in obj) {
//you could use key here if you like
break;
}
//key now contains your first key
or a shorter version should also do it:
for(var key in obj) break;
//key now contains your first key

Categories

Resources