I am interested in using vanilla Javascript objects and arrays to model data that has the data structure of a connected directed graph. One way to do this would be to use multiple object references within a single object tree to represent distinct edges of the graph. However, to use this efficiently, there needs to be a way to easily check what are the incoming edges to a given object (or put another way, for each object to know what all of it's parents are, and to know the property keys from each parent to itself.)
According to this older Stack Overflow post there is no built-in way to do this in Javascript: Get all object references in Javascript.
I've started making my own library called Parent Aware Objects that uses Proxy objects to intercept basic object assignment operations in order to keep track all the references to each object.
Before I go too far down this path, I wanted to ask if there might be an easier way to accomplish this.
This is the functionality I want:
const fido = {
name: 'Fido'
};
const alan = {
name: 'Alan',
pet: fido
}
const sarah = {
name: 'Sarah',
dogWalkerFor: fido
}
getParents(fido)
The function call getParents(fido) should return a list of entries that contains the alan object with key "pet" and sarah object with key "dogWalkerFor".
You would need a collection of all the nodes in the graph to search for:
const graphNodes = [fido, alan, sarah];
Then you can easily get all nodes that reference Fido:
graphNodes.filter(node => Object.values(node).includes(fido))
Of course, searching the entire graph every time is not really efficient. To do that, you would need to store the backedges on the node itself, or in a Map or something where you can easily look them up. This would then require updating that data structure every time you update your graph.
Related
Scenario:
For ex, you define a schema in realm-js as:
export const businessSchema = {
name: 'Business',
primaryKey: 'id',
properties: {
id: 'int',
number_of_stores_in_cities: 'int[]',
}
}
You have already created an object of Business and saved to Realm. Then have queried this object somewhere else. And you want to copy this field number_of_stores_in_cities to javascript array js_number_of_store_in_cities for further processing.
What I expect: Realm has defined handlers to get the entire target list which I can simply call it like number_of_stores_in_cities.all() or number_of_stores_in_cities.getList() in the Proxy
What I have: Seems like they didn't define these handlers for what I expect. And their getters are defined based on index of an array. So this proxy number_of_stores_in_cities works exactly like a javascript array. I tried to copy entries one by one from number_of_stores_in_cities to js_number_of_stores_in_cities. I've also tried const js_number_of_stores_in_cities = Array.prototype.slice.call(number_of_stores_in_cities). However, both of the methods are unexpectedly slow such that they took roughly 10 seconds to copy a length of 2500 list.
What I need: Instead of using these traditional javascript array methods, is there a way that I can get a plain javascript array from the Proxy in a fast fashion? Do I need to provide my handler to this Proxy?
I'm using #ngrx/store for an Angular 2 app.
My store holds a list of say, Book objects. I want to update a field in one of those objects. I also happen to have an Observable of the Book instance I'm looking to update (say, selectedBook).
To do the update I intend on calling the reducer with an UpdateBookAction, and a payload of the new Book. So I make a deep copy of the existing Book object by subscribing to selectedBook and then calling Object.assign().
But when I try to write to one of the fields of the copy I get the following error. (It happens to be the same error I get if I were to try to write directly to the Book object in the store.)
Error
Cannot assign to read only property 'name' of object '#<Object>' at ViewWrappedError.BaseError [as constructor]
Code
ngOnInit() {
this.book$ = this.store.let(fromRoot.getSelectedBook);
//...
}
someFunction() {
//...
this.book$.subscribe(book => {
let updatedBook = Object.assign({}, book);
updatedBook.name = 'something else'; // <--- THIS IS WHAT THROWS
let action = new BookUpdateAction(updatedBook);
this.store.dispatch(action);
}
}
Clarification after Comments
I was under the assumption that I could have an action with a payload that was not the entire state of the store. (In fact that seems necessary, no?) I'm confident that this is the case given the documentation.
The action I'm looking to take is something like this:
Action = UPDATE, payload = {'id': 1234, 'name': 'something new'}
As mentioned, I intend on making that call like this:
this.store.dispatch(action);
Presumably under the hood, ngrx is passing my action to the reducer along with the (immutable) current state.
So from there, everything should work okay. My logic inside the reducer doesn't mutate the existing state, it simply creates a new one out of the existing state and the payload I've passed in.
The real question here is how I can reasonably build the new "objectToUpdate" such that I can pass that in as the payload.
I could do something like this:
this.book$.subscribe(book => {
let updatedBook = new Book();
updatedBook.id = book.id;
//set all other fields manually...
updatedBook.name = 'something else';
let action = new BookUpdateAction(updatedBook);
this.store.dispatch(action);
}
But we're not just talking about two fields here... what if my book has several fields? Do I have to manually build from scratch a new Book each time just to update one field?
My solution was to do a deep copy using Object.assign({}, book) (and not mutate the old one!) and subsequently make the update to solely the field I was looking to touch.
The idea of the ngrx store is to have one and only one single place of truth, which means all the objects are immutable, and the only way to change anything is to recreate everything as a whole. Also, you are probably using the ngrx freeze (https://github.com/codewareio/ngrx-store-freeze) which means that all of the objects will be created read-only so you wont be able to change any (This is good for development if you want to completely follow the redux pattern). If you remove the part where the store freezes the object, you will be able to change it, but thats not best practice.
What I would suggest you is the following: Use the ngrx observable with async pipe to put the data (in your case books) in a dumb component which can only get input and output some event. Than, inside of the dumb component you can "edit" that object by making a copy of it, and after you are done, you can emit back the changes to the smart component which is subscribed to the store and allow it to change the state via the store (commit). This way is best because it is not very common to change the whole state for a really small change (like two way binding, when user types..).
If you follow the redux pattern, than you will be able to add history, which means the store will keep a copies of the last X state recreations, so you can get UNDO functionality, easier to debug, timeline etc
Your problem is that you are directly editing the property instead of recreating the whole state.
I'll have to make an assumption about the actual scenario the OP is experiencing.
The problem
It's not possible to modify a member of a frozen object. Its the error being thrown.
The cause
ngrx-store-freeze is used as a meta-reducer to freeze any object that enters the store. On another place, when an object needs to be changed, a shallow copy is being made. Object.assign() doesn't do deep copy. A member of another object reached from the original object is being modified. This secondary object is also frozen, by it is not duplicated.
Solution
Use a deep copy like cloneDeep() from lodash. Or sent a bag of properties to be changed with a proper action. Process the changes on the reducer.
As already mentioned - the reason you are getting
Cannot assign to read only property 'name' of object
is because 'ngrx-store-freeze' freezes the state and prevents mutating it.
Object.assign will provide a new object as you expect, but it will copy the state's properties along with each property's own definition - such as the 'writable' definition (which 'ngrx-store-freeze' likely sets to false).
A different approach is described in this answer and explains how cloning objects with JSON.parse(JSON.stringify(yourObject)) as fastest, but this approach has flaws if you keep dates or methods etc' in your state.
using lodash's 'cloneDeep' is probably your best bet for deep cloning the state.
One way to accomplish this is a utility/helper method to make a new book from.
You could give it an existing book and the subset of properties you want to add to a new book (using Partial in typeScript if you want type safety).
createNewBook(oldBook: Book, newProps: Partial<Book>): Book {
const newBook = new Book();
for(const prop in oldBook) {
if(newProps[prop]) {
newBook[prop]=newProps[prop];
} else {
newBook[prop]=oldBook[prop];
}
}
return newBook
}
You could call it via newBook = createNewBook(new Book(), {title: 'first foo, then bar'});
and use this newBook to update your store.
I wasnt quite sure what to call this question but here is what i want to do:
I am currently creating a series geneator for chartjs that will help me create my datasets.
now the way i want to do it is by simply using object keys to extract data from each element in my array.
Each element of an array could look something like this:
as you can see this object contains other objects inside of them.
This creates a problem because say i want the name of the object feedback_skill i would have to do the following:
data.forEach(function (x) {
x['feedback_skill']['name']
});
Which cannot be hold into one variable.
Now what i could do is pass the following array: serieKey = ['feedback','name'] suggesting that the first element in the array is the first key and the next element is the variable i want to hit.
However these datasets can have an unlimited number of layers so my question to you guys is:
Is there a smart way of doing this?
I'm not aware of a native JavaScript way of doing this, but various JavaScript frameworks allow you to access deep-properties from objects like this. For example Dojo has lang.getObject and I can see that there is a JQuery plugin that does something similar, lodash as well. If you're not using these frameworks, then you could always create your own util function to perform something similar.
These types of utility function allow you to pass the target as a "dot-notation" property, so you could call:
lang.getObject("feedback_skill.name", false, x)
Using Dojo for example, but they're all much of a muchness.
I don't see any problem with your approach, unlimited number of layers can be handled in the following manner :
data.forEach(function(x){
for(i in seriesKey)
x = x[seriesKey[i]]; // x will contain whatever you wanted to retrieve when the loop ends
doSomething(x);
}
seriesKey can be an array like the one in your example, with as many elements as you need to traverse to the depth you want.
Basically everything is said with the title: how to get the object from array by object's property value which is unique by the way?
Situation:
My current strategy is that I gave id attribute to the div of that object's property id (all ids are unique, take a look at Array of objects below) which looks like this:
<div id="3590" class="my-div"></div>
Now, when this div is clicked, I'll get the id attribute and I need to find the right object because I also need to get some other properties and also make some changes to some properties.
Why Im doing this:
This seems to be the only way because as I was told in other question that there's no way to access the object, even when this div is one of the object's properties (take a look at Array of objects below).
If that's not true, please let me know. This is super important!
Array of objects:
0: myObject
__e3_: Object
div: div#3590.my-div
gm_accessors_: Object
gm_bindings_: Object
id: 3590
latlng: _.K
map: _.ng
uh: Object
__proto__: c
1: myObject
__e3_: Object
div: div#3591.my-div
gm_accessors_: Object
gm_bindings_: Object
id: 3591
latlng: _.K
map: _.ng
uh: Object
__proto__: c
//thousands of objects
There can be thousands of objects in that array and that's why I added word "fastest" to the question: Im concerned about the performance because there's also other stuff going on.
Also, I prefer vanilla JS because Im currently learning it but if you know a good and fast way in jQuery, please go ahead, I'll convert it myself.
More details:
Objects are in array because I also need to iterate them (more often than working with them one-by-one)
All one-by-one actions are done via click events (user triggered and there's some protection: only one action at a time)
Keep reference to the corresponding object in the element as well.
So, whenever you click an element, the element object itself will have the reference to corresponding object. No need to find from the array.
And yes please be careful with name collisions. Assign object to new key so that object doesn't replace value of element object's existing key.
For old browsers, I am not sure they can even render thousands of elements.
Example:
var element = document.createElement("div");
element.myObj = {id: 3306, name: 'coolBoy'}; //make sure key, 'myObj' in this case, doesn't already exist.
var myArray = [];
myArray.push(element.myObj); //if you need to keep in array as well.
element.onclick = function(e){
console.log(e.currentTarget.myObj.name);
};
I'd recommend using a Map collection - it allows for both fast iteration and fast access by key, and is a builtin data structure.
If you don't want to go down the ES6 route (and not use a shim either), a reasonably fast approach would be to keep the array sorted. You can use binary search to access single elements.
Notice that for a thousand objects, there probably won't be an observable difference anyway. Don't prematurely optimise. Just keep your API to access the collection clean so that you can easily swap out the data structure in the future, should there be any need.
function getObjectFromId(id) {
return ARR.filter(function (obj) {
return obj.id === id;
})[0];
}
parent = {
child0: {
data1:'foo',
data2: 'bar'
},
child1: {
data1:'foo',
data2: 'bar'
},
child2: {
data1:'foo',
data2: 'bar'
}
}
At first I was thinking I would set a parent:child key since I will need that data individually of its siblings. In some instances though, I will need to return all of the data within parent.
Should I just put the whole object in a parent key?
Are there downsides to this if a lot of gets and sets may only be for one of it's children?
Is there a way to call all parent data with a parent:child schema?
Thanks!
Try a hash - that gives you HGET to get just one child and HGETALL to get all of them.
Storing the whole object as JSON in a single key is also valid though, and keeps your code simple if your usage is a good fit. If the numbers aren't too large, it may make sense to always retrieve the whole object even when you only need to display one child object.
The main reason to avoid storing complex objects in a single key is write conflicts - if two connections can modify different children of one object at the same time a hash will be much less trouble.
You might consider taking advantage of the hash data type. Using the parent as a key to the hash and using (HGET key field) for a particular child or (HKEYS key) for all the children.
It would be interesting if someone would post benchmarks for hash commands HSET and HGET. The benchmarks for the list operations (LPUSH 88109.25 /sec) are (~23%) slower though (SET 114293.71 /sec). Presumably HSET is yet slower though listed O(1).
So I think you would speed optimize the decision by looking at the ratio of full family requests to individual child request in your code.