I've been trying to teach myself FRP (and bacon.js specifically) by diving in head first on a new project. I've gotten pretty far on my own but recently ran into a problem that I can't seem to fight my way through:
I have an interface with a set of clickable objects. When an object is clicked, detailed information for that object is loaded in a panel to the right.
What I need is the ability to select multiple, to accumulate those objects into an array and show a "bulk actions" panel when more than one is selected.
So far I have:
a SelectMultiple boolean property that represents the current UI mode
a CurrentObject stream that holds the currently selected object
I've gotten somewhat close with this:
var SelectedObjects = CurrentObject.filter(SelectMultiple).skipDuplicates().scan([], function(a,b){
return a.concat([b]);
};
There are a few problems:
The value of SelectedObjects represents the objects selected over
all time, it doesn't reset when SelectMultiple state changes.
The value of SelectObjects does not include the original
CurrentObject (of course because the scan accumulator seed is an
empty array, not the current value of CurrentObject).
The fact that I'm looking to use the current value of a property directly seems to be a hint that there's a fundamental issue here. I have a notion that the answer involves flapMapLatest and spawning a new stream every time SelectMultiple changes, funneling selected orders into this new stream and accumulating, but I can't quite work out what that should look like.
Of course there is an additional problem that skipDuplicates only skips consecutive duplicates. I can probably work this one out on my own but a solution that addresses that issue would be ideal.
Any suggestions would be greatly appreciated!
This might work (coffeescript):
var selectMultiple # Property[Boolean] - whether in multiselect mode
var selectedObject # Property[Object] - latest selected object
var selectedObjects = selectMultiple.flatMapLatest((multiple) ->
if !multiple
selectedObject.map((obj) -> [obj])
else
selectedObject.scan([], (xs, x) ->
xs.concat(x)
)
).toProperty()
On each value of selectMultiple flag we start a new stream that'll either just track the current single selection or start accumulating from the single selection, adding items as they're selected. It doesn't support de-selection by toggling, but that's straightforward to add into the scan part.
Ok I figured out a solution. I realized that I could use a dynamically-sized slidingWindow combinator. I found the basis for the answer in the Implementing Snake in Bacon.js tutorial.
I got an error when I tried adding directly to the Bacon prototype (as described in the tutorial) so I just made a function that takes the stream to observe and a boolean that determines if it should capture values:
slidingWindowWhile = function(sourceStream, toTakeOrNotToTake) {
return new Bacon.EventStream(function(sink){
var buf = [];
var take = false;
sourceStream.onValue(function(x){
if (! take) {
buf = [];
}
buf.push(x);
sink(new Bacon.Next(buf));
});
toTakeOrNotToTake.onValue(function(v){
take = v;
});
});
};
It still seems like there should be a way to do this without using local variables to track state but at least this solution is pretty well encapsulated.
Related
I just started learning Linked List for a technical interview, so this question might seem a little weird.
I was reading an introduction article about Linked List from freecodecamp, and this is what the article
In JavaScript, a linked list looks like this:
const list = {
head: {
value: 6
next: {
value: 10
next: {
value: 12
next: {
value: 3
next: null
}
}
}
}
}
};
My question is, is this a real Linked List? Say I get a question "Print out all the elements in the following linked list, and implement a Linked List yourself." Can I just use the above code? I do know how to use classes to implement a linked list, but I am just wondering if the above code counts as a linked List.
I am asking this question because I only know how to solve the array algorithm question so far.
Say I want to print out all the elements in the array. I will need three steps.
Create an array. // Nums = [1,2,3];
write a function to print it out. // function printNums(Nums){ for (...){console.log(Nums[i]}}
call the function. // printNums(Nums);
So now, I want to do a Linked List version of this. How should I do it?
New Update:
So this is my LinkedList version of printing out all the elements. As a comment mentioned, what I did in the code is in fact a linked list, but it's not called implementation. But what if I just want to test my function? Does the following code make sense to you guys?
My question is, is this a real Linked List?
Yes, it is. You have a collection of data elements, where each data element points to the next.
Say I get a question "Print out all the elements in the following linked list, and implement a Linked List yourself." Can I just use the above code?
When asked to print a specific linked list, we should assume that it is made clear what the exact interface is of that linked list (e.g. nodes are linked via a property called next, and their values are accessed via a property called value). In case of the example list, that is clear. So yes, you could use the piece of code you provided.
The question to implement a Linked List yourself, is a different one. Although it could be understood to define one particular list, that is not how most would interpret that question. It is more likely that this means you should implement a linked list interface: i.e. write code that not only provides easy ways to construct any linked list, but also for using it (find a value, insert a node, delete a node, move a node, ...)
I am just wondering if the above code counts as a linked List.
Yes, the object literal you provided is (one particular instance of) a linked list.
Say I want to print out all the elements in the array. I will need three steps.
Create an array. // Nums = [1,2,3];
Here you make use of a feature of the JavaScript language. Implicitly a constructor is behind this: new Array(1, 2, 3). This constructor is provided as part of the core language. You also get access to methods like push, pop, indexOf, slice, splice, ...etc. All out of the box.
The difference with linked lists is that core JavaScript does not offer an implementation for it. You have to throw your own. Sure, you can use an object literal to create one linked list, but it is quite a verbose way (imagine a list with 100 numbers), and error prone (what if you misspelled next somewhere half way?)
So now, I want to do a Linked List version of this. How should I do it?
If the purpose is only to print the content of a linked list, and nothing else, you can do it like you did. But to get something that offers an interface like you get out of the box for arrays, you would write a class with some useful methods, so you could write:
let myList = LinkedList.from(6, 10, 12, 3);
console.log(...myList.values()); // outputs 6 10 12 3
But what if I just want to test my function? Does the following code make sense to you guys?
Yes, that is fine. If you know that your print function will get either null or an object that has value and next properties, where the next property can be null or another such object, ... then it is fine. In other words: you need to know the interface.
var l = head;
while (l != null) {
console.log(l.value);
l = l.next;
}
Trick is to use while loop until next element is null.
let prev: LinkedListElement<T> | null = this._start;
let finalEl: LinkedListElement<T> = prev;
let counter = 0;
while (prev !== null) {
const retVal = callback(prev, counter);
if (!retVal) {
break;
}
finalEl = prev;
prev = prev.next;
counter++;
}
Let me know if this works.
Also look at this LinkedList class I have created, traverse function is matching the requirement you have.
Class GitHub
NPM package - #dsinjs/linked-list
Complete documentation
I have a selectedItem object in Angular, it contains other objects and arrays. I create a deep copy using a JSON trick:
$scope.editableItem = JSON.parse(JSON.stringify($scope.selectedItem))
Then I use editableItem model in inputs, change some values inside. selectedItem doesn't change. Then I want to send via PATCH all the changes made, but not the fields which were not changed. So I need to strip the editableItem from all fields that are the same in unchanged selectedItem.
How to do this efficiently? I was thinking about traversing object recursively using Underscore, but I'd really like to know if it's a good way of thinking before I tackle it.
Alternatively I could probably create third object which would only contain touched fields from the second one, added dynamically, but I'm not sure how to approach this.
EDITED:
To be clear, I expect the answer to be generic and assume the most complicated object structure possible. For example no answers from this question are applicable here as they either assume the object has only simple fields or they need to have Angular watcher explicitly set for every field separately.
I do something similar with a function like this:
function getUpdateObject(orig, current) {
varChanges = {};
for (var prop in orig) {
if (prop.indexOf("$") != 0 && orig[prop] !== current[prop]) {
varChanges[prop] = current[prop];
}
}
return varChanges ;
};
I don't think this will get you all the way there. I'm not using it in any scenarios where the objects have member objects or arrays, but you should be able to test if "prop" is an object or array and call it recursively. The biggest caveat I see to that approach is if you have a deep, nested structure, you may not detect a change until you're down several levels. You'd probably have to keep the full potential hierarchy for a changed property in memory, then when you detect a change at a lower, level, write the whole hierarchy to the output object.
This is what I ended up with. Maybe it'll help someone. I used DeepDiff library. Code is in CoffeScript, should be easy to translate to JavaScript if anyone needs it.
$scope.getChangesObject = () ->
selected = $scope.selectedItem
editable = $scope.editableItem
changes = {}
differences = DeepDiff(selected, editable)
for diff in differences
formattedPath = ""
for pathPart, index in diff.path
if index isnt diff.path.length - 1
formattedPath += pathPart + "."
else
formattedPath += pathPart
changes[formattedPath] = editable[formattedPath]
changes
I’m making a collection of React Elements and displaying them; what follows is a trivial example to frame the problem of how-would-one-modify-an-preexisting-instantiated-element only.
var c = [
<div>A</div>,
<div>B</div>,
// ...
<div>Z</div>
];
var ListComponents = React.createClass({
render: function() {
return <div>{c}</div>;
}
});
ReactDOM.render(<ListComponents/>, document.getElementById('root'));
While the code above “works,” it renders a console message I’d rather not ignore:
Warning: Each child in an array or iterator should have a unique "key" prop.
Check the render method of `ListComponents`.
See https://fb.me/react-warning-keys for more information.
Superficially, I could just add a unique key="…" string to each element in c and be done with it.
However, that seems a quite verbose, especially since I have the data in an indexed array and a functional language that in theory can assign each key its matching index value without manually having to enter it as a source literal.
I’d love to be able to just do this...
c.forEach( (e,i) => e.key = i ); // ...or call some setter
What’s the *right* React-way to do this -and- keep the code clean?
ADDENDUM:
...for the curious or those that want to just say add a key field...
The collection I'm using is actually an array of tuples containing meta-data and a corresponding React Element, a custom Component, or some huge JSX block. The example above overly trivializes what the actual data looks like as well as its irregularities.
As the source data itself is quite long, updated often, and not maintained by a developer; it is highly error prone to missed key fields or duplicates values from manual entry. Hence the desire to do it entirely programmatically. I can not count on the data owners to do it properly. They can't read code, so ideally I'd rather not mess up the data structures with a lot of "programming goop."
The collection is manipulated a few times, putting various runs of certain elements into other dynamically created wrappers, so that the final collection is actually generated by a few transformations, filters, and maps before it is ultimately displayed.
A major shout out to Wes Bos, who came up with a clever solution that works!
The code is a simple one liner and does exactly what I was looking for:
c = c.map( (el,key) => React.cloneElement(el, {key} ));
We're building a new collection using the .cloneElement() method, which I was unaware of. That was what I needed, it turns out.
In the .map() operation, the lambda function is passed both the element and the index. It's return value is a cloned element, but with the key property set.
By cleverly naming the index element key, it allows the short notation for the expression { "key" : key }. This object augments the cloned object.
In the end, I end up with a new collection of identical objects, each with a key property set to the index.
When I do a push or pop operation on my observable array, it is reflected in the ui. However other operations on the array won't change anything in the UI. Here's an example of my case:
<ul data-bind="foreach: addresses">
<!-- ko template: {name: 'AddressItemTemplate', data: {address: $data, page: 'update-page'} }-->
<!-- /ko -->
</ul>
I use my template in two different pages and thats the reason I am using the template data like that.
<script type="text/html" id="AddressItemTemplate">
<p data-bind="text: (page == 'update-page') ? 'updating' : 'declined'"</p>
<p data-bind="text: address.title"></p>
</script>
Now on js side, ofc I declared the addresses as an observable array
this.addresses = ko.observableArray([addresObject1, addressObject2, ...])
Somewhere on the page, I edit the address values. To have UI reflecting the changes, I do the following:
//suppose we know that the first address is being edited
var tmp_addresses = addresses();
tmp_addresses[0].title = 'blabla';
addresses(tmp_addresses);
And there it is, in the viewModel, I can see that the content of the addresses has been updated, but not in the UI??
addresses.push(someAddressObject);
or
addresses.pop();
works (updates the UI with the new/removed element). But addresses.splice(0, 1, newAddressObject) does not do anything in the UI again.
What am I missing here? How can push pop work and not the others??
Am I experiencing a bug in knockout framework?
UPDATE
I found out a way to do it, but there's something wrong. I'll come to that but first:
I am well aware that if I use observable objects in the observable array, the changes would be reflected in UI. However that is exactly the thing I want to avoid. It is an overkill.
Observable properties should be required in cases where properties are really exposed to user interaction. For example, if you have a UI for setting each of the fields of an object, then yes, observable property would be the right call.
However in my case, I dont even have a UI for updating the address field. Moreover, I dont need tinkering and constantly watching all the properties of all the addresses. In my case, every now and then an update occurs from the server and that changes only a single field in a single address field.
On another perspective the way I suggest should work. I simply update the whole array at once, not every element individually. It's the exactly the same logic with:
someObservableObject({newObject: withNewFields, ...});
Thats why I dont need my objects as observables. I simply want to re-declare the array and be done with the change. For example, it is advised that if you are going to make lots of pushes into the observable array, dont use array.push(...) multiple times, instead re-declare the larger array on to the observable array variable in a similar way I do it in my question. Otherwise, I am telling knockout to track every single object and every single field in them, which is hardly what I want.
Now, I finally got it working but the way I do suggests that there is a cleaner way to do it.
I found out that, the items in the observable array are somehow tracked and not updated when you re-declare the array with them. For example the code I gave in the question would not work. However the code below works:
var tmp_addresses = addresses();
var tmp_addr = tmp_addresses[0];
var new_addr = {};
Object.keys(tmp_addr).forEach(function(key){
new_addr[key] = tmp_addr[key];
});
new_addr.title = 'Hey this is something new!'
addresses.splice(0, 1, new_addr);
Not satisfied? The code below is going to work as well, because we are re-defining the array:
var newAddressObject1 = {...}, newAddressObject2 = {...};
addresses([newAddressObject1, newAddressObject2]);
But the following would not work!
var tmp_addresses = addresses();
var tmp_addr = tmp_addresses[0];
tmp_addr.title = 'Hey this address wont update';
addresses.splice(0, 1, tmp_addr);
How come? I think knockout adds an internal property to his items in observableArrays and when I try to reinsert one, it will not update.
My problem has now morphed into creating a new object with the same properties of the desired item in the observable array. The way I coded above is simply very dirty-looking. There's gotta be a better way to do that
You are wrongly assigning value to observable title that is the reason why UI not reflecting its changes (2 way binding broken).
Thumb rule is always use () notation while assigning a value to observable (keeps two way binding intact)
viewModel:
var ViewModel = function () {
var self = this;
self.addresses = ko.observableArray([{
'title': ko.observable('one')
}, {
'title': ko.observable('two')
}])
setTimeout(function () {
var tmp_addresses = self.addresses();
tmp_addresses[0].title('blabla'); //assigning data to observable
self.addresses(tmp_addresses);
}, 2000)
};
ko.applyBindings(new ViewModel());
working sample here
PS: Don't get deceived by seeing the value change in viewModel the moment you done assigning using = two binding is broken UI wont reflect VM'S changes .
when you splice up your observableArray UI takes it changes check here
The problem was exactly as #jason9187 pointed out in the comments: The references of the objects in the observable array does not change when I edit a field of them. Therefore, KO would not interpret my array as changed. If the observableArray had contained simple data types, then the way I suggested could work without a problem. However, I have an Object in the array, therefore although I edit the Object, it's reference (pointer) remains the same, and KO thinks that all Objects are the same as before.
In order to achieve what I wanted, we have to solve the deep cloning problem in javascript like in this post.
Now there's a trade-off there, deep cloning is very simple in vanilla if you don't have a circular architecture or functions in your objects. In my case, there's nothing like that. The data comes from a restful API. If anybody in the future gets hold of this problem, they need to deep-clone their 'hard-to-clone' objects.
Here's my solution:
var tmp_addresses = JSON.parse(JSON.stringify(addresses())); //Creates a new array with new references and data
tmp_addresses[0].title = 'my new title';
addresses(tmp_addresses);
Or, if you can create address objects, following will work as well:
var tmp_addresses = addresses();
tmp_addresses[0] = new randomAddressObject();
addresses(tmp_addresses);
Here is a fiddle that I demonstrate both of the methods in a single example
I have two tables holding game data for two different games. For the sake of simplicity, let's say they only share a column called Timestamp (in particular they have a different number of columns). I want to render a list holding information from both tables, simultaneously ordered by Timestamp.
What I'm currently doing works, but I'd take almost any bet that there is a much better way to do this. I'm mostly concerned about performance at some point (mobile app). This is a stub representing the structure – believe me, I know how horrible this looks right now. I just wanted to make it work first, now I'm looking for improvements. ;)
var readyA,
readyB = false;
var dataA,
dataB;
function doLoop () {
setTimeout(renderData, 100);
}
function renderData () {
if (!readyA || !readyB) {
doLoop();
return;
}
var dataAll = dataA.concat(dataB);
dataAll.sort(function (a,b) {
return a['Timestamp'] <= b['Timestamp'];
});
// pass data into a template depending on from which game it is and render it
// ...
}
// wait for both queries to finish
doLoop();
// select data from game A
myDatabaseClass.query('SELECT ... FROM GameA', function (results) {
dataA = new Array(results.rows.length);
for (var i=0; i<results.rows.length; i++) {
dataA[i] = results.rows.item(i);
}
readyA = true;
});
// select data from game B
myDatabaseClass.query('SELECT ... FROM GameB', function (results) {
dataB = new Array(results.rows.length);
for (var i=0; i<results.rows.length; i++) {
dataB[i] = results.rows.item(i);
}
readyB = true;
});
The question would now be if I can somehow simplify this by some kind of UNION or JOIN in the query. Obviously, the Timeout construction is horrible, but that will automatically collapse to a simple callback function if the querying can be done in one query (or at least one transaction – the database class can handle that).
Edit: I did found this ( Pull from two different tables and order ) but this whole NULL AS some_column feels dirty. Is there really no better alternative?
The result of a query always is a single table with a fixed number of columns, so all the SELECTs must have the same number of columns:
SELECT a1, a2, a3, Timestamp FROM GameA
UNION ALL
SELECT b1, b2, NULL, Timestamp FROM GameB
ORDER BY Timestamp
(UNION ALL is faster than UNION because it doesn't try to remove duplicates.)
Your code is pretty good. From the point of view of a SQL hacker like me you're doing the UNION and the ORDER BY on the client side. There's nothing wrong with that. You seem to be doing it almost right. Your "concat" is the client-side equivalent of UNION, and your "sort' is the equivalent of ORDER BY.
You say that the NULL as missing-column construction feels somehow dirty if you use server-side UNION operations. But, obviously to treat two different result sets as the same so you can sort them in order you have to make them conform to each other somehow. Your a['Timestamp'] <= b['Timestamp'] sort-ordering criterion in your sort function is also a scheme for conforming two result sets to each other. It may be lower-performance than using a UNION.
Don't be afraid of using NULL as missing-column to make two result sets in a UNION conform to each other. It's not dirty, and it's not expensive.
Do consider limiting your SELECT operation somehow, perhaps by a range of timestamps. That will allow your system to scale up, especially if you put an index on the column you use to limit the SELECT.
(By the way, your sort function has a mistake in it. sort functions need to return -1, 0, or +1 depending on whether the first item is less than, equal to, or greater than the second one. You're returning a true/false value. That doesn't work properly.)
(You're parallelizing the two queries to the same MySQL instance. That's clever, but probably in truth is a formula for overloading MySQL as your game scales up. Keep in mind that each user of your game has her own machine running Javascript but they all share your MySQL.)