How to copy nested state between objects in React - javascript

In a react app, I have state that looks like this:
dates: {"2015-11-20":{
"13:00":{...},
"14:00":{...}},
"2015-11-21":{
"18:00":{...},
"19:00":{...}}}
I'm running into a problem with updating state - my use case is I want users to be able to copy all the times (e.g. 1pm & 2pm above) to other dates via the UI and have state update accordingly.
So far, all my attempts result in referencing, where the separate objects become linked and any update to one results in an update to the other.
Sample code that produces the broken behaviour:
copyTimes: function() {
var keys = [];
Object.keys(this.state.dates).map(function(key) {
keys.push(key);
});
for(var i in keys) {
this.state.dates[keys[i]] = this.state.dates[this.state.selectedDate];
}
this.setState({dates: this.state.dates});
}
I believe I might need to use the react add on 'update' to solve this, as per https://facebook.github.io/react/docs/update.html
but I can't fully understand the syntax approach I need to take. How do I cause a correct update to 'copy' the contents of one date: '2015-11-20' to another e.g. 2015-11-21 in the above example?
EDIT
As per (pointed out in comments): What is the most efficient way to deep clone an object in JavaScript?
copyTimes: function() {
var keys = [];
var tempStart = JSON.parse(JSON.stringify(this.state.dates));
Object.keys(this.state.dates).map(function(key) {
keys.push(key);
});
for(var i in keys) {
tempStart[keys[i]] = JSON.parse(JSON.stringify(this.state.dates[this.state.selectedDate]));
}
this.setState({dates: tempStart});
},
The above works - but is extremely ugly. Is there a better approach?

copyTimes: function() {
var keys = [];
Object.keys(this.state.dates).map(function(key) {
keys.push(key);
});
var newDates = Object.assign({}, this.state.dates);;
for(var i in keys) {
newDates[keys[i]] = this.state.dates[this.state.selectedDate];
}
this.setState({dates: newDates});
}

Related

Differences between js objects

I got the following problem and I am looking for a really efficient way to do this.
I got two Javascript Objects always build like {id:data,id:data,..}
If I only look on the Keys they will look like this:
B = ["1","2","3"]
A = ["2","3","4"]
Now I need the information what i need to do, to transform B into A, so in this case: Delete B.1 and B.4 = A.4 .
I was thinking that maybe a prototyp function for Object would be a good way to do this.
This is what i have so far:
Array.prototype.diff = function(a) {
return this.filter(function(i) {return a.indexOf(i) < 0;});
};
Object.prototype.syncTo = function(b,callbackA,callbackB){
var a = this;
var bKeys = Object.keys(b);
var aKeys = Object.keys(a);
var toremove = bKeys.diff(aKeys);
var toadd = aKeys.diff(bKeys);
for(var i = 0; i < toremove.length; i++) {
if(b.hasOwnProperty(toremove[i])) {
delete b[toremove[i]];
}
}
callbackB(b);
for(var i = 0; i < toadd.length; i++) {
if(a.hasOwnProperty(toadd[i])){
<<Dont know how to go on now>>
}
}
callbackA(XXXXXX);
};
Where CallbackA should be called with all elements that have to be added to B and CallbackB should be called with all elements that need to be removed from B.
I am struggling With the elements for callbackA and in general whether this is an efficient way of doing this.
Thank you for your support !
EDIT:
An Example for one of the Callbacks would be :
callbackB:
function (items){
for(var i in items){
items[i].removeSomeWhereElse();
}
}
There are a couple of libraries that can do this if your search NPM, as a shameless plug I'll just mention one I authored that diffs any object, including array insertion/deletion/moves:
https://github.com/benjamine/jsondiffpatch
here's the DEMO page diffing 2 arrays, as you need:
http://benjamine.github.io/jsondiffpatch/demo/index.html?desc=moving%20around&left=%5B0%2C1%2C2%2C3%2C4%2C5%2C6%2C7%2C8%2C9%2C10%5D&right=%5B10%2C0%2C1%2C7%2C2%2C4%2C5%2C6%2C88%2C9%2C3%5D
you can see deletes, adds, and even moves (move detection can be disabled by configuration if you want)
Using library will be the more efficient in saving your time, now if you want to save CPU cycles instead, you could just use a simple implementation of LCS (which is the standard algorithm to solve to the problem you're describing), see: https://en.wikipedia.org/wiki/Longest_common_subsequence_problem
jsondiffpatch includes that (for js), and you can steal it from here: https://github.com/benjamine/jsondiffpatch/blob/master/src/filters/lcs.js

Compare two lists and remove common elements using angular js

I am having 2 lists like below
list1 =
['option1','option2','option3','option4','option5','option6','option7'];
list2 = ['option3', 'option4', 'option7'];
I want the list to be
listFinal = ['option1','option2','option5','option6','option7'];
Please give me the suggestion using angular js how to solve this using filter
Tried to use this code to solve this using filter but unable to succeed.
app.filter('unique', function() {
return function(collection, keyname) {
var output = [],
keys = [];
angular.forEach(collection, function(item) {
var key = item[keyname];
if(keys.indexOf(key) === -1) {
keys.push(key);
output.push(item);
}
});
return output;
};
});
I'm not sure that you really want a filter here. This is something that you should probably solve outside of Angular. If you're willing to add lodash to your project, you could do this:
var diff = _.difference(array1, array2);
If you must use a filter, you could try something like this?
app.filter('difference', function() {
return function(input, diff_array) {
var result = [];
for (var i = 0; i < input.length; i++) {
if (diff_array.indexOf(input[i]) == -1) {
result.push(input[i]);
}
}
return result;
}
}
Note that if you do this, your template has to look something like:
{{ input | difference:diff_arr }}
I haven't actually tested any of this, but this should be the general idea. I concur with Phillip that this really isn't something you should try to solve in Angular.
What you are describing is an "array/set difference". There are many questions on StackOverflow asking about this already. This is not related to Angular.js, but is an problem with an algorithmic solution. Just to add bit of interesting material, a "naive" solution runs in O(nlogn) which would be:
Sort the two lists/arrays from lowests to highest
Traverse each array setting the internal pointer to 0 initially, compare the elements. If the element in Array A is larger than the on in Array B advance the pointer by 1 in Array B, else B, else if they are equal, remove the result from the array and advance both pointers
What is the fastest or most elegant way to compute a set difference using Javascript arrays?
JavaScript array difference

Is there a non loop-every-single-list-item approach to find unique list items?

I know I could use the loop-every-single-list-item approach to filter out unique elements in a given list, but I feel like there's probably a neat, quick way to do it.
How can I find unique list items in JavaScript, without looping through and filtering them manually?
Lately I was working on event handling patch and needed fast method for filtering out unique function handlers in a callback lists which got to be run quite frequently.
Here's what I'm trying to do:
Array.prototype.unique = (function () {
// main Array#unique method
var uni = function uni () {
return this.filter(uni.x);
};
// attach a helper for resolving unique elements
// if element is at current position, not before,
// it's unique one, pass `true` flag to .filter()
uni.x = function (node, pos, ls) {
return pos === ls.indexOf(node);
};
// save
return uniq;
})();
Implementation:
// sample list:
// generate ~1K long list of integers:
// get the keys of string object of length 32,
// map every item to key-list itself,
// flatten, shuffle..
var ls =
Array.prototype.concat.apply([],
Object.keys(new String('1'.repeat(32)))).
map(function (node, pos, list) { return list; }).
sort(function () { return Math.random() < Math.random(); });
// run each function 1K times fetching unique values
for (
var
it = -1,
l = 1000,
// record iteration start
tm = Date.now();
++it < l;
ls.unique()
);
No. If you have a list, you will need to look at least once at every single item to determine whether it is unique.
If you need something faster, don't use a list.
Btw, even on a list you can implement a unique-algorithm in less than the O(n²) that you currently have. See Easiest way to find duplicate values in a JavaScript array for some clever approaches.
I was working on event handling patch and needed fast method for filtering out unique function handlers in a callback list which got to be run quite frequently.
Then you don't want to put them in that list in the first place. Don't check the list for duplicates when you run it (which as you say is frequent), but when you insert a new handler.
If you think that using .indexOf to find a handler in the list is still too slow, you can mark every function object that it is already contained in the list. Choose a unique (per list) property name, and put a value on that property of each function that is in the list. You can then check in constant runtime for duplicates.
If you have a unique key, using a dictionary is a good option. However, if you have some logic that needs to be executed to perform your filtering, I'd go with UnderscoreJS. Check out the _.filter method. It's a great library with lots to offer in this area.
http://underscorejs.org/#filter
I don't think there is a way to get unique list of items without iterating through each item. If you're looking for a built-in library function, I don't think there is one in Angular.
It would be simple enough to create one:
function unique(array, fn) {
var hash = [];
var list = [];
for(var i = 0; i < array.length; ++i) {
var key = fn(array[i]);
if (key && !hash[key]) {
list.push(array[i]);
hash[key] = key;
}
}
return list;
}
Usage:
var myList = [ { id:1, name="oranges"},
{ id:2, name="apples" },
{ id:1, name="oranges"},
{ id:3, name="pears" },
{ id:3, name="pears" } ];
var uniqueList = unique(myList, function(item) { return item.id; });

Javascript 'First or Default' function for 'associative arrays'/objects

Is there a better way to do this?
I'm storing values in what some would erroneously call an associated array:
The tokens object stores.... tokens and a count of documents using that token on a per-db level.
var tokens = {'db1' : { '654321': { 'docCount': 1 },
'321456': { 'docCount': 2 } },
'db2' : { '999999': { 'docCount': 1 } } };
I can add/remove dbs and tokens and update the docCounts appropriately.
We can assume, due to code omitted for brevity, that if a db exists, a token also exists with a docCount of at least 1.
If a db exists and I need to retrieve ANY of its tokens, what is the best method?
If the dbs held arrays, it would be as easy as tokens['db1'][0]... but I'm not using arrays.
I have something like the following, "inspired" by LINQ (please don't blame LINQ):
// NOTE: default not implemented here
var firstOrDefault = function(obj) {
var thing;
for (var i in obj) {
thing = i;
break;
}
return thing;
};
which would be called as so (simplified for example):
var anyToken;
if (tokens['db1') { anyToken = firstOrDefault(tokens['db1']); }
Generally returning per the above example '654321' (as this is an object, not an array, order is not guaranteed, but either value is acceptable in my code).
Is this a reasonable method to get any value?
Is there a better method?
Should I just suck it up, shove everything into an array, and wrap the storage features that way?
UPDATE: I've removed the default reference, as an unfound item will a perfectly acceptable undefined response:
// NOTE: obj.hasOwnProperty not implemented for brevity
var firstOrAny = function(obj) {
var thing;
for (var i in obj) {
thing = i;
break;
}
return thing;
};
which would be called as so (simplified for example):
var anyToken;
if (tokens['db1') { anyToken = firstOrAny(tokens['db1']); }
Slightly shorter solution:
var firstOrDefault = function(obj, d) {
for (var i in obj)
{
if (obj.hasOwnProperty(i))
{
return obj[i];
}
}
return d;
};
But yes, it is the fastest way to get any (usually first inserted) key from an object.
I also added a hasOwnProperty check to prevent cases where the values are retrieved from the prototype chain.

knockout.js computed function to create two arrays?

I have an array of objects that a user can perform search on. I'm using a ko.computed function based on the search to create another array of matched items for display.
self.matchedRecords = ko.computed(function() {
return ko.utils.arrayFilter(self.transponders(), function(r) {
return r.title.toLowerCase().indexOf($('.search-input').val().toLowerCase()) > -1
}
};
This works great and I'm really impressed with the performance so far.
This issue is I also need the "unmatched" records because I have to perform an addition operation on them in some cases (60% of the time). I don't really want to create a second ko.computed function because then I have to run through this array a second time every time a search is performed.
So, my question: Is there a way that I can use the same ko.computed to create a second array of unmatched items? Basically run through the array and put each item in the matched or unmatched array...
If not, is it faster to:
1) create a second ko.computed to get the unmatched items from my array as the user searches; or
2) write an arrayDiff function and determine the unmatched items on demand if I need them.
Cheers!
If you are worried about performance, you could have a non-observable array that you populate while you iterate the search results in your computed. Also note that you are repeatedly selecting using jQuery inside your loop, which I think negates any KO-caused slowdowns.
self.missedRecords = [];
self.matchedRecords = ko.computed(function() {
var searchQuery = $('.search-input').val().toLowerCase(),
transponders = self.transponders(),
matched = [];
// Clear out missed records
self.missedRecords.length = 0;
_.each(transponders, function(transponder) {
if (transponder.title.toLowerCase().indexOf(searchQuery) >= 0) {
matched.push(transponder);
} else {
self.missedRecords.push(transponder);
}
});
return matched;
});
I used _.each from Underscore to keep the code shorter. The drawback of this approach is that changes to missedRecords can't (reliably) be bound to the UI (e.g. in case you have a foreach binding).
If you do need the missedRecords array to be observable, and still want to keep things fast(er), you could do something like this:
self.missedRecords = ko.observableArray([]);
self.matchedRecords = ko.computed(function() {
var searchQuery = $('.search-input').val().toLowerCase(),
transponders = self.transponders(),
matched = [],
missed = [];
_.each(transponders, function(transponder) {
if (transponder.title.toLowerCase().indexOf(searchQuery) >= 0) {
matched.push(transponder);
} else {
missed.push(transponder);
}
});
// Clear out missed records, without triggering subscriptions
self.missedRecords().length = 0;
// Copy the local missed array to the KO observable array
// This will NOT trigger notifications
ko.utils.arrayPushAll(self.missedRecords(), missed);
// Tell KO that the observable array has mutated - this will trigger changes
// to anything observing the missedRecords array
self.missedRecords.valueHasMutated();
return matched;
});
You could also skip computed altogether and just subscribe to changes to change the state of your arrays. For example:
self.missedRecords = ko.observableArray([]);
self.matchedRecords = ko.observableArray([]);
self.transponders.subscribe(function(newTransponders) {
var matched = [],
missed = [];
_.each(newTransponders, function(transponder) {
// Populate matched/missed local arrays
});
// Copy the arrays to the observableArray instances using the technique above
});

Categories