how to create a hashtable of json objects in javascript? - javascript

I want to create a hashtable of json objects, where each json object represents a user.
I want to do this to sort of create a client side 'cache' of users.
User { ID: 234, name: 'john', ..);
So I can then reference things like this:
if(userCache[userid] != null)
alert(userCache[userid].ID);
is this possible?

Javascript objects themselves are maps, so for instance:
var userCache = {};
userCache['john'] = {ID: 234, name: 'john', ... };
userCache['mary'] = {ID: 567, name: 'mary', ... };
userCache['douglas'] = {ID: 42, name: 'douglas', ... };
// Usage:
var id = 'john';
var user = userCache[id];
if (user) alert(user.ID); // alerts "234"
(It wasn't clear to me whether your "userid" would be "john" or 234, so I went with "john" above, but you could use 234 if you preferred.)
It's up to the implementation how the keys are stored and whether the map is a hash map or some other structure, but I've done this with hundreds of objects and been perfectly happy with the performance, even on IE, which is one of the slower implementations (at the moment).
This works because there are two ways to get at the properties of a Javascript object: Via dotted notation, and via bracketed notation. For example:
var foo = {bar: 10};
alert(foo.bar); // alerts "10"
alert(foo['bar']); // alerts "10"
alert(foo['b' + 'a' + 'r']); // alerts "10"
s = "bar";
alert(foo[b]); // alerts "10"
It may seem strange that this bracketed syntax for getting an object property by name is the same as getting an array element by index, but in fact, array indexes are object properties in Javascript. Property names are always strings (in theory), but auto-conversion occurs when you do things like user[234]. (And implementations are free to optimize-out the conversion if they can, provided the semantics are maintained.)
Edit Some bits and pieces:
Looping through the cache
And if you want to loop through the cache (and based on your follow-up question, you do, so perhaps others reading this question will want to too):
var key, user;
for (key in userCache) {
// `key` receives the name of each property in the cache, so "john",
// "mary", "douglas"...
user = userCache[key];
alert(user.ID);
}
The keys are looped in no defined order, it varies by browser.
Deleting from the cache
Suppose you want to delete a property from the cache entirely:
delete userCache['john'];
Now there is no longer a "john" property in the object at all.

This is almost verbatim your code, but it works, yes:
var cache = {}
cache[234] = { id: 234, name: 'john' }
if (cache[1]) alert('Hello ' + cache[1].name);
if (cache[234]) alert('Hello ' + cache[234].name);
Or was your question on how to implement this on the server side too, to get the user cache to the client?

function findUser(users, userid) {
for (var i = 0; i < users.length; i++) {
if (users[i].ID === userid) {
return users[i];
}
}
return null;
}
var users = [{ ID: 234, name: 'john' }, { ID: 235, name: 'smith' }];
var user = findUser(users, 235);
if (user != null) {
alert(user.name);
}

Since all objects in Javascript are really dictionaries, you can do this in a couple ways.
One way might be:
var dict = new Object;
dict.Bobby = { ID: 1, Name: "Bobby" };
if (dict.Bobby != null) alert(dict.Bobby.Name);

Related

Update Javascript Object only if there is a change [duplicate]

This question already has answers here:
How to determine equality for two JavaScript objects?
(82 answers)
Closed 2 years ago.
I have a javascript object on my DB, its personal data, this is the structure:
const storedUserData = {
name: "John",
sirname:"Doe",
phone: "666-666-66666",
streetName: "Fake Street",
streetNumber: "123",
zipCode: "90125"
}
and I have a separate object, which has the same structure, which basically is on the front end, and its the result of reading form data.
const formData = {
name: "John",
sirname:"Doe",
phone: "666-666-66666",
streetName: "Fake Street",
streetNumber: "123",
zipCode: "90125"
}
Basically, when the user clicks submit I want to check if there are differences between the stored object storedUserData, above, and the new object, formData. If there are differences, save the differences to the DB.
Of course, I could go on like this for each property, since there are few:
if(storedUserData.name !== formData.name) {
pushDataToDb()
}
but its the lazy approach, and I want to do it correctly. I've been reading into object keys, but I cant figure it out. How could I successfully loop between each property of both items comparing them, and then only if there is a change between the two properties I would push to DB.
Thank you.
You could use a loop as you mentioned above,
function isEquivalent(a, b) {
// Create arrays of property names
var aProps = Object.getOwnPropertyNames(a);
var bProps = Object.getOwnPropertyNames(b);
// If number of properties is different,
// objects are not equivalent
if (aProps.length != bProps.length) {
return false;
}
for (var i = 0; i < aProps.length; i++) {
var propName = aProps[i];
// If values of same property are not equal,
// objects are not equivalent
if (a[propName] !== b[propName]) {
return false;
}
}
// If we made it this far, objects
// are considered equivalent
return true;
}
Read more about it here

Duplication of items in a Javascript Set

I was going through basics of Javscript Set.According to its defintion, A Set is a special type collection – “set of values” (without keys), where each value may occur only once.
But I see when it comes to reference types the behavior is different. Consider the following snippet:
let set = new Set();
let john = { name: "John" };
let pete = { name: "Pete" };
let mary = { name: "Mary" };
set.add(john);
set.add(pete);
set.add(mary);
set.add(john);
set.add(mary);
console.log("Scenario 1");
for (let user of set) {
console.log(user.name);
}
let set1 = new Set();
set1.add({ name: "John" });
set1.add({ name: "Pete" });
set1.add({ name: "Mary" });
set1.add({ name: "John" });
console.log("Scenario 2");
for (let user of set1) {
console.log(user.name);
}
I see in the scenario 1, it wont allow duplicates to be added as they are the same references. But in scenario 2 I see duplicates are being added.
Can some explain this behavior? Or Am I missing something.
How scenario 1 is different from 2?
Try doing a check of {name: 'John'} === {name: 'John'}. You would find it returns false.
Every new object has a different reference even though the contents can be same. If it gives false to you, the Set would consider it a different element too.
When you assign a variable with a Reference value, its memory location gets copied.
For example:
let john = {name: 'John'} // lets say memory: XYZ
So, every time when you do: set.add(john);, you are adding the memory location in the set. So, the Set would see you adding XYZ everytime and it won't accept duplicates.
In the second case,
When you do:
`set1.add({ name: "John" });` // You added maybe XYF
`set1.add({ name: "John" });` // You added maybe XYN
So, your Set treats them differently and adds both of them.
A Set does not look at the contents of the object itself. It only looks at the pointer to the object.
If it's not a pointer to the same physical object, then it's allowed to be added to the Set as a different physical object. In fact, you can add an object to the set and then change it's contents afterwards because the fact that it's in the set has NOTHING to do with the contents of the object, only the physical pointer to the object. If it's not the same pointer to the same object, then it can be added separately.
Here's an example:
let s = new Set();
let x1 = {name: "John"};
let x2 = {name: "John"};
console.log(x1 === x2); // false, not the same physical object
s.add(x1);
console.log(s.has(x1)); // true
console.log(s.has(x2)); // false
s.add(x2);
console.log(s.has(x2)); // true
console.log(Array.from(s)); // [{name: "John"}, {name: "John"}];
// now modify x1
x1.name = "Bob";
// the x1 object is still in the set, even though you modified it
// because being in the set has NOTHING to do with the contents of the object at all
console.log(s.has(x1)); // true
console.log(Array.from(s)); // [{name: "Bob"}, {name: "John"}];

Design pattern to check if a JavaScript object has changed

I get from the server a list of objects
[{name:'test01', age:10},{name:'test02', age:20},{name:'test03', age:30}]
I load them into html controls for the user to edit.
Then there is a button to bulk save the entire list back to the database.
Instead of sending the whole list I only want to send the subset of objects that were changed.
It can be any number of items in the array. I want to do something similar to frameworks like Angular that mark an object property like "pristine" when no change has been done to it. Then use that flag to only post to the server the items that are not "pristine", the ones that were modified.
Here is a function down below that will return an array/object of changed objects when supplied with an old array/object of objects and a new array of objects:
// intended to compare objects of identical shape; ideally static.
//
// any top-level key with a primitive value which exists in `previous` but not
// in `current` returns `undefined` while vice versa yields a diff.
//
// in general, the input type determines the output type. that is if `previous`
// and `current` are objects then an object is returned. if arrays then an array
// is returned, etc.
const getChanges = (previous, current) => {
if (isPrimitive(previous) && isPrimitive(current)) {
if (previous === current) {
return "";
}
return current;
}
if (isObject(previous) && isObject(current)) {
const diff = getChanges(Object.entries(previous), Object.entries(current));
return diff.reduce((merged, [key, value]) => {
return {
...merged,
[key]: value
}
}, {});
}
const changes = [];
if (JSON.stringify(previous) === JSON.stringify(current)) {
return changes;
}
for (let i = 0; i < current.length; i++) {
const item = current[i];
if (JSON.stringify(item) !== JSON.stringify(previous[i])) {
changes.push(item);
}
}
return changes;
};
For Example:
const arr1 = [1, 2, 3, 4]
const arr2 = [4, 4, 2, 4]
console.log(getChanges(arr1, arr2)) // [4,4,2]
const obj1 = {
foo: "bar",
baz: [
1, 2, 3
],
qux: {
hello: "world"
},
bingo: "name-o",
}
const obj2 = {
foo: "barx",
baz: [
1, 2, 3, 4
],
qux: {
hello: null
},
bingo: "name-o",
}
console.log(getChanges(obj1.foo, obj2.foo)) // barx
console.log(getChanges(obj1.bingo, obj2.bingo)) // ""
console.log(getChanges(obj1.baz, obj2.baz)) // [4]
console.log(getChanges(obj1, obj2)) // {foo:'barx',baz:[1,2,3,4],qux:{hello:null}}
const obj3 = [{ name: 'test01', age: 10 }, { name: 'test02', age: 20 }, { name: 'test03', age: 30 }]
const obj4 = [{ name: 'test01', age: 10 }, { name: 'test02', age: 20 }, { name: 'test03', age: 20 }]
console.log(getChanges(obj3, obj4)) // [{name:'test03', age:20}]
Utility functions used:
// not required for this example but aid readability of the main function
const typeOf = o => Object.prototype.toString.call(o);
const isObject = o => o !== null && !Array.isArray(o) && typeOf(o).split(" ")[1].slice(0, -1) === "Object";
const isPrimitive = o => {
switch (typeof o) {
case "object": {
return false;
}
case "function": {
return false;
}
default: {
return true;
}
}
};
You would simply have to export the full list of edited values client side, compare it with the old list, and then send the list of changes off to the server.
Hope this helps!
Here are a few ideas.
Use a framework. You spoke of Angular.
Use Proxies, though Internet Explorer has no support for it.
Instead of using classic properties, maybe use Object.defineProperty's set/get to achieve some kind of change tracking.
Use getter/setting functions to store data instead of properties: getName() and setName() for example. Though this the older way of doing what defineProperty now does.
Whenever you bind your data to your form elements, set a special property that indicates if the property has changed. Something like __hasChanged. Set to true if any property on the object changes.
The old school bruteforce way: keep your original list of data that came from the server, deep copy it into another list, bind your form controls to the new list, then when the user clicks submit, compare the objects in the original list to the objects in the new list, plucking out the changed ones as you go. Probably the easiest, but not necessarily the cleanest.
A different take on #6: Attach a special property to each object that always returns the original version of the object:
var myData = [{name: "Larry", age: 47}];
var dataWithCopyOfSelf = myData.map(function(data) {
Object.assign({}, data, { original: data });
});
// now bind your form to dataWithCopyOfSelf.
Of course, this solution assumes a few things: (1) that your objects are flat and simple since Object.assign() doesn't deep copy, (2) that your original data set will never be changed, and (3) that nothing ever touches the contents of original.
There are a multitude of solutions out there.
With ES6 we can use Proxy
to accomplish this task: intercept an Object write, and mark it as dirty.
Proxy allows to create a handler Object that can trap, manipulate, and than forward changes to the original target Object, basically allowing to reconfigure its behavior.
The trap we're going to adopt to intercept Object writes is the handler set().
At this point we can add a non-enumerable property flag like i.e: _isDirty using Object.defineProperty() to mark our Object as modified, dirty.
When using traps (in our case the handler's set()) no changes are applied nor reflected to the Objects, therefore we need to forward the argument values to the target Object using Reflect.set().
Finally, to retrieve the modified objects, filter() the Array with our proxy Objects in search of those having its own Property "_isDirty".
// From server:
const dataOrg = [
{id:1, name:'a', age:10},
{id:2, name:'b', age:20},
{id:3, name:'c', age:30}
];
// Mirror data from server to observable Proxies:
const data = dataOrg.map(ob => new Proxy(ob, {
set() {
Object.defineProperty(ob, "_isDirty", {value: true}); // Flag
return Reflect.set(...arguments); // Forward trapped args to ob
}
}));
// From now on, use proxied data. Let's change some values:
data[0].name = "Lorem";
data[0].age = 42;
data[2].age = 31;
// Collect modified data
const dataMod = data.filter(ob => ob.hasOwnProperty("_isDirty"));
// Test what we're about to send back to server:
console.log(JSON.stringify(dataMod, null, 2));
Without using .defineProperty()
If for some reason you don't feel comfortable into tapping into the original object adding extra properties as flags, you could instead populate immediately
the dataMod (array with modified Objects) with references:
const dataOrg = [
{id:1, name:'a', age:10},
{id:2, name:'b', age:20},
{id:3, name:'c', age:30}
];
// Prepare array to hold references to the modified Objects
const dataMod = [];
const data = dataOrg.map(ob => new Proxy(ob, {
set() {
if (dataMod.indexOf(ob) < 0) dataMod.push(ob); // Push reference
return Reflect.set(...arguments);
}
}));
data[0].name = "Lorem";
data[0].age = 42;
data[2].age = 31;
console.log(JSON.stringify(dataMod, null, 2));
Can I Use - Proxy (IE)
Proxy - handler.set()
Global Objects - Reflect
Reflect.set()
Object.defineProperty()
Object.hasOwnProperty()
Without having to get fancy with prototype properties you could simply store them in another array whenever your form control element detects a change
Something along the lines of:
var modified = [];
data.forEach(function(item){
var domNode = // whatever you use to match data to form control element
domNode.addEventListener('input',function(){
if(modified.indexOf(item) === -1){
modified.push(item);
}
});
});
Then send the modified array to server when it's time to save
Why not use Ember.js observable properties ? You can use the Ember.observer function to get and set changes in your data.
Ember.Object.extend({
valueObserver: Ember.observer('value', function(sender, key, value, rev) {
// Executes whenever the "value" property changes
// See the addObserver method for more information about the callback arguments
})
});
The Ember.object actually does a lot of heavy lifting for you.
Once you define your object, add an observer like so:
object.addObserver('propertyKey', targetObject, targetAction)
My idea is to sort object keys and convert object to be string to compare:
// use this function to sort keys, and save key=>value in an array
function objectSerilize(obj) {
let keys = Object.keys(obj)
let results = []
keys.sort((a, b) => a > b ? -1 : a < b ? 1 : 0)
keys.forEach(key => {
let value = obj[key]
if (typeof value === 'object') {
value = objectSerilize(value)
}
results.push({
key,
value,
})
})
return results
}
// use this function to compare
function compareObject(a, b) {
let aStr = JSON.stringify(objectSerilize(a))
let bStr = JSON.stringify(objectSerilize(b))
return aStr === bStr
}
This is what I think up.
It would be cleanest, I’d think to have the object emit an event when a property is added or removed or modified.
A simplistic implementation could involve an array with the object keys; whenever a setter or heck the constructor returns this, it first calls a static function returning a promise; resolving: map with changed values in the array: things added, things removed, or neither. So one could get(‘changed’) or so forth; returning an array.
Similarly every setter can emit an event with arguments for initial value and new value.
Assuming classes are used, you could easily have a static method in a parent generic class that can be called through its constructor and so really you could simplify most of this by passing the object either to itself, or to the parent through super(checkMeProperty).

What are the possible usage scenarios for the new JavaScript "Symbol" datatype?

I just stumbled upon the documentation for the new (proposed for ES6, but already implemented in Firefox, Chrome & Opera) datatype in JavaScript, Symbol:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol
http://people.mozilla.org/~jorendorff/es6-draft.html#sec-symbol-objects
I'm reading about it, but I just can't think of a possible usage scenario.
The documentation says:
A symbol is a unique and immutable data type and may be used as an identifier for object properties.
OK, fine, let's say I do as the documentation says:
obj[Symbol("a")] = "a";
but, since Symbol('a') always returns unique value (object) and:
Symbols are not visible in for...in iterations.
how do I retrieve my property from obj?
var obj = { normalProperty: 'just a string' };
obj[Symbol('a')] = 'a';
document.getElementById('retrieve').addEventListener('click', function() {
document.write('Stringified object:' + JSON.stringify(obj) + '<br/><br/>');
document.write('Trying to get Symbol-property value, aaaand...: <br/>');
document.write(obj[Symbol('a')]); // undefined
}, false);
<button id="retrieve">Retrieve obj's property</button>
Edit
Of course, you can retrieve it like this:
var x = Symbol('a');
obj[x] = 'a';
obj[x]; // "a"
but what's the purpose of doing it this way?
Thanks in advance :)
After reading the documentation and playing a little bit with this Symbol type in chrome, it appears that a Symbol is a way to define a name -- not a value --, and the fact that properties defined using symbols are not visible using for..in, Object.getOwnPropertyNames() or JSON.stringify() makes symbols useful for metadata properties:
// define metadata symbols
var Metadata = {
Date: Symbol('Message date')
};
var email = function(recipient, message) {
this.Recipient = recipient;
this.Message = message;
this[Metadata.Date] = new Date();
};
var email1 = new email('#Me', 'test');
JSON.stringify(email1);
// {
// Recipient: '#Me',
// Message: 'test'
// }
// Date is still accessible using
email1[Metadata.Date];
// Thu Nov 27 2014 16:50:00 GMT+0000
// Debugging in Console:
// {
// Recipient: '#Me',
// Message: 'test'
// Symbol(Message date): Thu Nov 27 2014 16:50:00 GMT+0000
// }
Symbols can be made global using the Symbol.for function, so metadata names can be created once and used across all project files.
Accessing the value using a symbol requires having a reference to the symbol when created. Each call to Symbol() creates a new one even if the same description is used:
var a = Symbol('a');
var b = Symbol('a');
a != b
// and
a != Symbol('a')
but, creating a symbol using Symbol.for, it will be registered in a global registry and the description becomes a key, meanong only one symbol with the same key will exist in the global registry:
var a = Symbol.for('a');
var b = Symbol.for('a');
a == b
// and
a == Symbol.for('a')
They pretty much help us having naming collisions. Anytime that you want to create a property in a unique way, that's when you should reach for a symbol.
Take a look at my example
const bert = Symbol('Bert');
'Bert'
Note: this is not a value this is what they called a descriptor,
because the symbol itself is just a unique identifier. So if you were
to visualize what a symbol would be maybe you can visualize is as
something like this "sdfasdfa2342134987fgsdfgsdf9808fsfgsd" absolute
unique symbol so that you can make sure that it will never overrides
any other piece of code in there.
What's cool about this is if I create a second symbol, like
const person = Symbol('Bert')
You can see I used 'Bert' again. Are those going to be the same because I described them as the same thing?
const bert = Symbol('Bert');
const person = Symbol('Bert');
console.log(bert);
console.log(person);
console.log(bert === person);
console.log(bert == person);
This can be useful if you were creating an object of your class.
const classRoom = {
'Mia' : { grade: 50, gender: 'female' },
'Gilbert': { grade: 80, gender: 'male' },
'Gilbert' { grade: 80, gender: 'male' },
};
But then you have another named Gilbert, so you got a naming collision there. So imagine if you're working on million and millions of data. So rather than using the persons name or using some sort of unique identifier we can use a symbol to name them.
const classRoom = {
[Symbol('Mia')] : { grade: 50, gender: 'female' },
[Symbol('Gilbert')]: { grade: 80, gender: 'male' },
[Symbol('Gilbert')]: { grade: 80, gender: 'male' },
};
Another thing about symbols is that they are not enumerable, which means we cannot loop over them if I were to do
for (const person in classRoom) {
console.log(person);
}
I get nothing.
If you do want to get access to all your symbols because theyres some information that you want to get you can use the object method.
const syms = Object.getOwnPropertySymbols(classRoom);
const data = syms.map(sym => classRoom[sym]);
console.log(data);

Creating a (good) struct from a javascript typed array?

I'm thinking about typed arrays in javascript; in particular, reading binary file headers. Reading the MDN Typed Arrays documentation, they blithely say we can represent the C struct:
struct someStruct {
unsigned long id;
char username[16];
float amountDue;
};
with the javascript:
var buffer = new ArrayBuffer(24);
// ... read the data into the buffer ...
var idView = new Uint32Array(buffer, 0, 1);
var usernameView = new Uint8Array(buffer, 4, 16);
var amountDueView = new Float32Array(buffer, 20, 1);
and then you can say amountDueView[0] but seem oblivious to the fact that treating a scalar as a one-element array is terrible and being able to say something like sizeof(someStruct) is useful as well.
So... is there something better? MDN makes reference to js-ctypes but it's no standard javascript.
Edit: Yes, I know about javascript objects. Modeling the data isn't the problem; reading and writing binary data elegantly is.
For what it's worth, there's jBinary, which lets you do stuff like:
var binary_data = '\x0f\x00\x00\x00Bruce\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xecQXA';
var myTypeset = {
'jBinary.all': 'someStruct',
'jBinary.littleEndian': true,
someStruct: {
id: 'uint32',
username: ['string0', 16],
amountDue: 'float'
}
};
var binary = new jBinary(binary_data, myTypeset);
header = binary.readAll(); // Object {id: 15, username: "Bruce", amountDue: 13.520000457763672}
header.username = "Jim";
binary.writeAll(header);
binary.readAll(); // Object {id: 15, username: "Jim", amountDue: 13.520000457763672}
It's not particularly well-documented (an example like the above would be very welcome) but it's quite good.
The biggest caveat is that it relies on javascript objects having properties remain in the order they're defined, which is practically the case but not part of the javascript spec. I suspect this will never actually change because it would break a kajillion things, but still.
Can't you just define an object in JS to model this?
var someObject = {
Id:2,
username: [],
amountDue:5
};
You more than likely don't need typed arrays for your application; the rest of the world has been surviving just fine without them. IE < 10 doesn't even support them.
You can write the object like this
var data = {id: 123, username: "bob", amountDue: 123.45};
And read from it like this
data.id;
// 123
data.username;
// "bob"
Try to read an unset key
data.foo;
// undefined
And set a key after it's defined
data.foo = "bar";
// "bar"
And delete the key
delete data.foo;
// true
Try to read a deleted key
data.foo;
// undefined

Categories

Resources