I have 2 JS objects, each is rendered as Tree in a webpage.
My issue is how to force a change on one of them while user applies a change to others.
My basic idea is to "bind onChange" on each objects obviously paying attention to not generate infinite loops.
In jQuery it seems almost difficult, I read something about "proxy" but I don't understand if it could help me on this topic.
I lastly thought to vue.js. I read that vue.js is very efficient syncing js and dom objects so a change between them is almost easy, maybe is possible to sync two js objects?
To be clearer, here more details:
I have something like this:
let obj1={key1:1, key2:[1,2,3]}; // defines arbitrary data obj
let obj2={};
$.extend(obj2,obj1); // defines obj2 as clone of obj1
// do "something magic" here
I would like to get the following:
obj1.key1=2; // => should automatically set obj2.key1=2; under the hood
obj2.key2.push(4); // => should automatically set obj1.key2=[1,2,3,4] under the hood
Is there any trick to bind two (identical, cloned) data objects so that any change made on one of them is reflected to the other one, as if the involved object keys "pointed" to the same data? Since objects are assigned "by reference" in javascript, this is doable if we define a third object "obj_value" and we assign it as value to the above objects as follows:
obj1.key=obj_value; // both obj1.key and obj2.key point to the same object
obj2.key=obj_value;
But I'd like something more general, directly binding one obj key to the other, in pseudo-code:
obj1.on('change',function(key,value)
{
obj2.key=value;
})
watch: {
objChangedByUser: function (value) {
this.cloneOfobjChangedByUser = Object.assign({}, value);
}
}
Or:
computed: {
cloneOfObjChangedByUser: function () {
return Object.assign({}, this.objChangedByUser);
}
}
Related
I'm still learning JS. In some other languages, you can pass variables byref and then modify them elsewhere in code.
In an attempt to avoid having lots of duplicate code, I have structured a series of callbacks and parsing like so:
class MarketData {
constructor() {
//Arrays
this.OneMinuteData = [];
this.ThreeMinuteData = [];
this.initializeCandleData();
}
initializeData() {
var client = new Client();
this._initializeData(60, client, this.OneMinuteData);
this._initializeData(180, client, this.ThreeMinuteData);
}
_initializeData(granularity, client, dataStore) {
client.GetRates({ granularity: granularity }, function(err, msg, data) {
var items = data.map(item => ({
///data mapped here
}));
dataStore = dataStore.concat(items);
}
}
So essentially I have this 'private' _initializeData function with the hopes of passing in an array and having it add to the array, but since JS passes byval, I cannot achieve the desired effect (e.g. this.OneMinuteData array is not modified).
Because of this, the only way I currently know how to work around this problem is to essentially have the same function copy-pasted for each individual array, which I find incredibly sloppy. Is there a better way of doing this?
but since JS passes byval, I cannot achieve the desired effect (e.g. this.OneMinuteData array is not modified).
While JavaScript does pass by value, that value when dealing with an object (including any array) is a reference.
See the documentation for concat:
The concat() method is used to merge two or more arrays. This method does not change the existing arrays, but instead returns a new array.
So when you say dataStore = dataStore.concat(items);, you assign a new array to the local dataStore variable and discard the old one.
Outside the function, the original array is unchanged.
The reason the array assigned to OneMinuteData is not modified is because you never modify any array.
Push the values of items into dataStore instead.
dataStore.push.apply(dataStore, items);
NB: GetRates has the signature of an asynchronous function, so make sure you don't try to inspect the modifications to OneMinuteData before they are made.
I want to pass following payload to the API
params[field1]: value1
params[field2]: value1
....
params[fieldN]: valueN
I have field and value coming from an object.
var params = {};
jQuery.each($scope.linkParams, function(a, b) {
params.params[a] = b; // returns undefined variable error
// I also tried other options but all result in one or another error
// Some doesn't result into an erro but doesn't get merged. See below merge requirement
});
I also wants to merge the above created object to another object with
jQuery.extend(extraParams, params);
How to achieve the rquired object?
Update
$scope.linkParams = {
field1: 'value1',
field2: 'value2',
....
};
You have two questions, so I'll address them one at a time.
(For a TL;DR, I emboldened the solution text. Hopefully the rest is worth the read, though.)
Object Serialization is Pretty Magical, but Not Quite That Magical
If I had a JS object that I instantiated like the following:
var cat = {
'meow': 'loud',
'type': 'Persian',
'sex': 'male'
}
then it is certainly true that you get attribute reference for free. That is, you can say something like cat.meow, and your runtime environment will make sense of that. However, JS will not automatically create properties of an object that you have referenced do not exist, unless you are referencing them to create them.
cat.health = 'meek' will work, but cat.ears[0] = 'pointy' will not.
var cat = {
'meow': 'loud',
'type': 'Persian',
'sex': 'male'
}
cat.health = 'meek'
alert(cat.health)
cat.ears[0] = 'pointy'
alert(cat.ears[0])
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
You'll notice that the first alert happens and contains the expected value, but the second alert never comes. This is because the code fails on the line with cat.ears[0] = 'pointy', and it stops execution at that point.
This may seem to contradict what I just said, but look closely at what's happening. When we attempt to initialize the first element of cat.ears, we must reference the cat.ears property, which does not exist.
JS implementations won't assume that you want to create items up the chain eternally, which is likely by design -- if it didn't throw errors and instead just created any properties or objects that needed to exist in order for your program to by syntactically sound, many pieces of software would silently break when they failed to include required libraries. If you forgot to include JQuery, it'd just create a $, a JQuery variable, and all of the properties of those objects you reference in your code. It'd be a proper mess to debug.
In short, that's what you're -- probably accidentally -- assuming will work here. params.params is analogous to cat.ears in the above example. You may have some reason for doing this, but assuming you don't, your code should function if you simply change params.params[a] to params[a].
JQuery.extend()
Assuming that extraParams is a valid array/object, the code you have written will work once params doesn't break your code anymore, however: do note that this will modify your extraParams object. If you want a new object to contain both params and extraParams, write something more like:
var args = $.extend({}, params, extraParams)
That will modify an empty object and add in the contents of params and extraParams. See the JQuery documentation for more information.
Some manipulations and I was able to achieve the required results.
I am posting the code for further reference:
var d = {};
jQuery.each($scope.linkParams, function(a,b) {
a = "params[" + a + "]";
d[a] = b;
});
jQuery.extend(extraParams, d);
I'm calling a JavaScript function that wants an array of things to display. It displays a count, and displays the items one by one. Everything works when I pass it a normal JavaScript array.
But I have too many items to hold in memory at once. What I'd like to do, is pass it an object with the same interface as an array, and have my method(s) be called when the function tries to access the data. And in fact, if I pass the following:
var featureArray = {length: count, 0: func(0)};
then the count is displayed, and the first item is correctly displayed. But I don't want to assign all the entries, or I'll run out of memory. And the function currently crashes when the user tries to display the second item. I want to know when item 1 is accessed, and return func(1) for item 1, and func(2) for item 2, etc. (i.e., delaying the creation of the item until it is requested).
Is this possible in JavaScript?
If I understand correctly, this would help:
var object = {length: count, data: function (whatever) {
// create your item
}};
Then, instead of doing array[1], array[2], et cetera, you'd do object.data(1), object.data(2), and so on.
Since there seems to be a constraint that the data must be accessed using array indexing via normal array indexing arr[index] and that can't be changed, then the answer is that NO, you can't override array indexing in Javascript to change how it works and make some sort of virtual array that only fetches data upon demand. It was proposed for ECMAScript 4 and rejected as a feature.
See these two other posts for other discussion/confirmation:
How would you overload the [] operator in Javascript
In javascript, can I override the brackets to access characters in a string?
The usual way to solve this problem would be to switch to using a method such as .get(n) to request the data and then the implementor of .get() can virtualize however much they want.
P.S. Others indicate that you could use a Proxy object for this in Firefox (not supported in other browsers as far as I know), but I'm not personally familiar with Proxy objects as it's use seems rather limited to code that only targets Firefox right now.
Yes, generating items on the go is possible. You will want to have a look at Lazy.js, a library for producing lazily computed/loaded sequences.
However, you will need to change your function that accepts this sequence, it will need to be consumed differently than a plain array.
If you really need to fake an array interface, you'd use Proxies. Unfortunately, it is only a harmony draft and currently only supported in Firefox' Javascript 1.8.5.
Assuming that the array is only accessed in an iteration, i.e. starting with index 0, you might be able to do some crazy things with getters:
var featureArray = (function(func) {
var arr = {length: 0};
function makeGetter(i) {
arr.length = i+1;
Object.defineProperty(arr, i, {
get: function() {
var val = func(i);
Object.defineProperty(arr, i, {value:val});
makeGetter(i+1);
return val;
},
configurable: true,
enumerable: true
});
}
makeGetter(0);
return arr;
}(func));
However, I'd recommend to avoid that and rather switch the library that is expecting the array. This solution is very errorprone if anything else is done with the "array" but accessing its indices in order.
Thank you to everyone who has commented and answered my original question - it seems that this is not (currently) supported by JavaScript.
I was able to get around this limitation, and still do what I wanted. It uses an aspect of the program that I did not mention in my original question (I was trying to simplify the question), so it is understandable that other's couldn't recommend this. That is, it doesn't technically answer my original question, but I'm sharing it in case others find it useful.
It turns out that one member of the object in each array element is a callback function. That is (using the terminology from my original question), func(n) is returning an object, which contains a function in one member, which is called by the method being passed the data. Since this callback function knows the index it is associated with (at least, when being created by func(n)), it can add the next item in the array (or at least ensure that it is already there) when it is called. A more complicated solution might go a few ahead, and/or behind, and/or could cleanup items not near the current index to free memory. This all assumes that the items will be accessed consecutively (which is the case in my program).
E.g.,
1) Create a variable that will stay in scope (e.g., a global variable).
2) Call the function with an object like I gave as an example in my original question:
var featureArray = {length: count, 0: func(0)};
3) func() can be something like:
function func(r) {
return {
f : function() {featureArray[r + 1] = func(r + 1); DoOtherStuff(r); }
}
}
Assuming that f() is the member with the function that will be called by the external function.
I have an object that represents a restaurant order:
function order () {
this.customer_name = ''
this.menu = // menu object
}
extended with some object methods for business logic, like:
order.prototype.value = function() {
var total = 0;
for (var i = 0; i < this.menu.length; i++) {
// calculate the order value
}
return total;
}
In the angular controller orders get pushed onto an array when submitted (via ng-click from a button in the view):
var ref = new Firebase('https://myfirebase.firebaseio.com');
$scope.orders = [];
angularFire(ref, $scope, 'orders');
$scope.currentOrder = orderService;
$scope.submitOrder = function() {
$scope.orders.push($scope.currentOrder);
};
Once orders are pushed into the array, properties like orders[0].customer_name work, but methods like orders[0].value() don't.
It seems reasonable that Firebase/Angularfire would only be syncing JSON, but is there an approach that would allow me to keep order-related logic included with the order object, i.e without having to write $scope.getOrderValue(orders[0])?
There isn't a great way to do exactly what you want, since according to the Firebase FAQ:
At a low-level, we support basically the same data types as JSON: Strings, Numbers, Booleans, and Objects (which in turn contain Strings, Numbers, Booleans, and more Objects).
Which means you can store data but not functions. It seems like a clean way to accomplish the same thing would be to store the latest order value as a property of your order object, and have a method as part of your orderService that updates it whenever menu items are added or removed. Alternatively, do what you suggested and have a getOrderValue somewhere, but it probably still makes sense to put that in a service.
I actually had the same issue.
I wanted to add a method to my firebase object.
After looking in the latest angularfire docs I found that $extend can do just that
I didn't test it yet, but I think this is the way to go about it.
Is there simple immutable hash and array implementation in javascript? I don't need best speed, a reasonable speed better than a clone would be good.
Also, if there are simple implementations in Java or some other languages that can be easily understood and ported to JavaScript, it would be also nice.
UPDATE:
The goal isn't to just froze the hash (or array), but to make an efficient implementation of update operation - update of immutable hash should return a new immutable hash. And it should be more efficient than doing it by "clone original and update it".
Native JS types have complexity of update something like O(1), with cloning the complexity will be O(n), with special immutable data structures (what I asked for) it will be 0(log(n))
UPDATE2: JavaScript already has Array / Hash :
Yes, but they are mutable, I need something similar but immutable, basically it can be done very simply by cloning hash2 = hash1.clone(); hash2[key] = value but it's very inefficient, there are algorithms that made it very efficient, without using the clone.
hash1 = {}
hash2 = hash1.set('key', 'value2')
hash3 = hash1.set('key', 'value3)
console.log(hash1) // => {}
console.log(hash2) // => {key: 'value2'}
console.log(hash3) // => {key: 'value3'}
SOLUTION:
It's not an implementation for immutable hash, but more like a hack for my current problem, maybe it also helps someone.
A little more about why I need immutable data structures - I use Node.js and sort of in-memory database. One request can read database, other update it - update can take a lot of time (calling remote services) - so I can't block all read processes and wait until update will be finished, also update may fail and database should be rolled back. So I need to somehow isolate (ACID) read and write operations to the in-memory database.
That's why I need immutable arrays and hashes - to implement sort of MVCC. But it seems there is a simpler way to do it. Instead of updating database directly - the update operation just records changes to database (but not perform it directly) - in form of "add 42 to array db.someArray".
In the end - the product of update operation will be an array of such change commands, and because it can be applied very quickly - we can block the database to apply it.
But, still it will be interesting to see if there are implementation of immutable data structures in javascript, so I'll leave this question open.
I know this question is old but I thought people that were searching like me should be pointed to Facebook's Immutable.js which offers many different types of immutable data structures in a very efficient way.
I had the same requirements for persistent data structures for JS, so a while ago I made an implementation of a persistent map.. https://github.com/josef-jelinek/cofy/blob/master/lang/feat.js
It contains implementation of a balanced tree based (sorted) map, and a naive copy-on-write map (and unfinished persistent vector/array).
var map = FEAT.map();
var map1 = map.assoc('key', 'value');
var value = map1.get('key');
var map2 = map1.dissoc('key');
...
it supports other methods like count(), contains(key), keys(into = []), values(into = []), toObject(into = {}), toString()
The implementation is not too complicated and it is in public domain. I accept suggestions and contributors too :).
Update: you can find unit tests (examples of usage) at https://github.com/josef-jelinek/cofy/blob/master/tests/test-feat.html
Update 2: Persistent vector implementation is now there as well with the following operations: count(), get(i), set(i, value), push(value), pop(), toArray(into = []), toString()
The only way to make an object immutable is to hide it inside a function. You can then use the function to return either the default hash or an updated version, but you can't actually store an immutable hash in the global scope.
function my_hash(delta) {
var default = {mykey: myvalue};
if (delta) {
for (var key, value in delta) {
if (default.hasOwnProperty(key)) default[key] = value;
}
}
return default;
}
I don't think this is a good idea though.
The best way to clone an object in Javascript I'm aware of, is the one contained in underscore.js
Shortly:
_.clone = function(obj) {
if (!_.isObject(obj)) return obj;
return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
};
_.extend = function(obj) {
each(slice.call(arguments, 1), function(source) {
for (var prop in source) {
obj[prop] = source[prop];
}
});
return obj;
};