Immutable.js without redux, how do I create new lists without explicitly naming them? - immutable.js

---Background---
I'm trying to learn current best data management practices and as far as I can tell that means stateless / stateful components and immutable data structure. I'm having problems with implementing latter (immutables). I'm trying to incorporate it into angular 2 without redux. Redux is on my list of things to learn but for now I want to use immutable.js without redux.
---The problem---
How do I create a copy of an array in a service and return it on demand? I have this example code (just for illustration purposes, I haven't tested it!):
import { Product } from './product';
import { Immutable } from './immutable';
export class ProductListService {
let id = 0;
const cheese = new Product(id++, 'cheese');
const ham = new Product(id++, 'ham');
const milk = new Product(id++, 'milk');
// I fill the list with some sample data
let oldProductList = Immutable.List.of(cheese, ham, milk);
let newProductList = [];
let returnProductList = oldProductList;
getProductList() {
return returnProductList;
}
addProduct() {
// As far as I know, this creates a deep immutable copy
newProductList = oldProductList.withMutations(function (list) {
list.push(new Product(id++, 'name'););
});
returnProductList = newProductList;
oldProductList = newProductList;
}
}
The above code is based on the example from the official docs where they just add a number to the variable each time they create a copy (I understand that is only for example purposes?). How do I go about creating new lists without using numbers? Do I use oldList / newList? Do I dynamically create new numbers for new variables so that I have a history of objects?
I feel I'm doing something wrong on a architectural level here. What is the correct approach? All immutable.js examples are using redux or show no real-life example, does someone know of a good material to learn about immutalbe.js (+ possible ng2?)
Thanks

Not sure i fully understand what you want to do here,
but consider this:if you just want to push one element to the list you should not use withMutations.
let list1 = Immutable.List(['one'])
let list2 = list1.push('two')
console.log(list1.toJS()) // ['one']
console.log(list2.toJS()) // ['one', 'two']
Applying a mutation to create a new immutable object results in some overhead, which can add up to a minor performance penalty. use withMutations only If you need to apply a series of mutations locally before returning
let list1 = Immutable.List(['one'])
var list2 = list1.withMutations(function (list) {
list.push('two').push('three').push('four').push('five');
});
console.log(list1.toJS()) //["one"]
console.log(list2.toJS()) //["one", "two", "three", "four", "five"]
here we create a temporary mutable (transient) copy of list 1 and apply a batch of mutations in a performant manner by using withMutations
I hope that answers your question

Related

How can I filter only on elements in the Autodesk Forge Viewer API that are relevant for quantity take off?

For BIM model quantity take off I need to filter on all elements that are relevant for quantity take off. Below are some visual examples of the element I need (green circled) and which elements I don't need (red cross)
Example 1
Example 2
How can I filter only on elements in the Autodesk Forge API that are relevant for quantity take off?
When I use the API I get all levels and I didn't manage to get the level I need.
First of all, you'll need to define the logic to filter these elements.
From the images you've shared, the hierarchy tree of your model seems to be organized in a way that you're interested in the nodes in the 5th level from the root node.
So, if that's the case, you could use something like the snippet below to find the nodes' paths and then filter it to retrieve the 5th-level nodes' dbids:
var findfifthNodes = function (model) {
return new Promise(function (resolve, reject) {
model.getObjectTree(function (tree) {
let nodes = {};
tree.enumNodeChildren(tree.getRootId(), function (dbid) {
let nodepath = [dbid];
if(!!nodes[tree.getNodeParentId(dbid)]){
nodepath = nodepath.concat(nodes[tree.getNodeParentId(dbid)]);
}
nodes[dbid] = nodepath;
}, true /* recursively enumerate children's children as well */);
let fifthNodes = Object.values(nodes).filter(p => p.length == 5).map(a => a[0]);
resolve(fifthNodes);
}, reject);
});
}
You can also add your own custom tree view that might be more suitable to your workflow.
To achieve that, please refer to https://aps.autodesk.com/blog/custom-tree-views

ES6 module immutability

I thought ES6 module exports were always immutable, so I'm pretty confused about the behaviour I'm getting. I have a simple array of colors that I would like to use in multiple components of my Vue application. It is in it's own file like so:
export const colors = [
'#ffb3ba',
'#ffdfba',
'#ffffba',
'#bae1ff',
]
Then I import it into the component where I want to use it like this:
import { colors } from '../assets/colors';
I have a function for picking a random color and then removing it from the list so it isn't picked again for the same component. It's something like this.
descriptions.forEach(item => {
const colorIndex = Math.floor(Math.random() * colors.length);
item['color'] = colors[colorIndex];
colors.splice(colorIndex, 1);
});
The idea here is to pick a random color from the list, assign it a description and then remove it from the list so a different one is picked on the next iteration of the forEach.
The problem is that it seems to be removing the colors from the list permanently. So when I import and try to use the array in another component, there are no colors in it. How can I make it so there is a fresh instance of the colors array for every component?
The imported bindings are not assignable, that's all. They are similar to const - you cannot change the variable, but you can mutate the object it holds. To prevent that, freeze the object when exporting it:
export const colors = Object.freeze([
'#ffb3ba',
'#ffdfba',
'#ffffba',
'#bae1ff',
]);
How can I make it so there is a fresh instance of the colors array for every component?
Have a look at Copying array by value in JavaScript for that: just colors.slice(). Also you'll want to check out How to randomize (shuffle) a JavaScript array? for how to efficiently get the random colors for your descriptions - there are even some answers that do not mutate the input.
import { colors } from '../assets/colors';
import { shuffle } from '…';
const randomColors = shuffle(colors.slice());
console.assert(descriptions.length <= randomColors.length);
for (const [i, description] of descriptions.entries())
description.color = randomColors[i];
ES6 module imports are not immutable, as you have correctly observed.
You could create a shallow copy of the array and operate on that one:
const copiedColors = [...colors];
descriptions.forEach(item => {
const colorIndex = Math.floor(Math.random() * colors.length);
item['color'] = copiedColors[colorIndex];
copiedColors.splice(colorIndex, 1);
});

How do I format my AngularJS data model?

Hi I am just beginning with angular and I am struggling to find the answer to what I'm sure is quite a simple thing to do.
I am currently getting the values of some input boxes and pushing them into my scope. This is creating one long 'array' eg:
['data-1','data-2','data-3']
I would like to format my data in the following way instead
$scope.data = [
{
'header1': 'data1-1',
'header1': 'data1-2',
'header1': 'data1-3'
},
{
'header1': 'data2-1',
'header1': 'data2-2',
'header1': 'data2-3'
}
]
This is my function as it currently is.
$scope.createRow = function(){
angular.forEach(angular.element("input"), function(value, key){
$scope.td.push($(value).val());
});
}
Any help or pointers would be greatly appreciated as I am just getting my head round the angular way
Doing this isn't hard... but before I give you a gun to shoot yourself in the foot, just to say that I think it would be beneficial to explain WHY you want structure in that other format you are mentioning. You seem to have lots of data repetition and that's always a red flag.
Now for the code, you just need to create object before pushing it to the array like:
$scope.createRow = function(){
angular.forEach(angular.element("input"), function(value, key){
var obj = {
"header1": val + "-1",
"header2": val + "-2"
};
$scope.td.push(obj);
});
}
EDIT:
OK, so you are trying to add new row to the table. First of all, you shouldn't be doing angular.forEach, but rather those input elements in HTML should bind to existing scope object, like:
// obviously use better names than Input1Value
// I am here just giving you example
$scope.bindData = {
Input1Value: null,
Input2Value: null
};
// in HTML you will do
// <input ng-model="bindData.Input1Value" />
// <input ng-model="bindData.Input2Value" />
Now that you've eliminated that nasty angular.forEach you need to have some kind of event handler, for example when user clicks the button you want to add this object to the array to which table is data bound. Just be sure to clone the $scope.bindData object when you add it to array.
$scope.createRow = function(){
var newRowData = $scope.cloneObject($scope.bindData);
$scope.td.push(newRowData);
}
// http://heyjavascript.com/4-creative-ways-to-clone-objects/
// https://stackoverflow.com/questions/728360/most-elegant-way-to-clone-a-javascript-object
$scope.cloneObject = function(objToClone) {
var newObj = (JSON.parse(JSON.stringify(objToClone)));
}
To close this answer off - keep in mind, if you ever find yourself directly referencing HTML DOM elements in Javascript with AngularJS - you are doing something wrong. It's a nasty habit to eliminate, especially if you are coming from jQuery background (and how doesn't?), where everything is $("#OhHiThere_ElementWithThisId).
Obviously the main thread on this topic on StackOverflow is this one:
“Thinking in AngularJS” if I have a jQuery background?
However I find that it's too theoretical, so Google around and you may find better overviews like:
jQuery vs. AngularJS: A Comparison and Migration Walkthrough

Special JSON binding in WinJS ListView

I have problems binding this JSON to my list view.
http://pubapi.cryptsy.com/api.php?method=marketdatav2
No data is displayed.
Data.js
(function () {
"use strict";
var _list;
WinJS.xhr({ url: 'http://pubapi.cryptsy.com/api.php?method=marketdatav2' }).then(
function (response) {
var json = JSON.parse(response.responseText);
_list = new WinJS.Binding.List(json.return.markets);
},
function (error) {
//handle error
}
);
var publicMembers =
{
itemList: _list
};
WinJS.Namespace.define("DataExample", publicMembers);
})();
HTML:
<section aria-label="Main content" role="main">
<div id="listItemTemplate" data-win-control="WinJS.Binding.Template">
<div class="listItem">
<div class="listItemTemplate-Detail">
<h4 data-win-bind="innerText: label"></h4>
</div>
</div>
</div>
<div id="listView" data-win-control="WinJS.UI.ListView" data-win-options="{itemDataSource : DataExample.itemList, itemTemplate: select('#listItemTemplate'), layout: {type: WinJS.UI.GridLayout}}"></div>
</section>
I feel that the API is not that well formed.
Isnt this part a bit odd?
"markets":{"ADT/XPM":{...}...}
There are three things going on in your code here.
First, a ListView must be bound to a WinJS.Binding.List's dataSource property, not the List directly. So in your HTML you can use itemDataSource: DataExample.itemList.dataSource, or you can make your DataExample.itemList dereference the dataSource at that level.
Second, you're also running into the issue that the declarative binding of itemDataSource in data-win-options is happening well before DataExample.itemList is even populated. At the point that the ListView gets instantiated, _list and therefore itemList will be undefined. This causes a problem with trying to dereference .dataSource.
The way around this is to make sure that DataExample.itemList is initialized with at least an empty instance of WinJS.Binding.List on startup. So putting this and the first bit together, we have this:
var _list = new WinJS.Binding.List();
var publicMembers =
{
itemList: _list.dataSource
};
With this, you can later replace _list with a different List instance, and the ListView will refresh itself.
This brings us to the third issue, populating the List with your HTTP response data. The WinJS.Binding.List takes an array in its constructor, not an object. You're passing the parsed JSON object straight from the HTTP request, which won't work.
Now if you have a WinJS.Binding.List instance already in _list as before, then you can just walk the object and add items directly to the List as follows:
var jm = json.return.markets;
for (var i in jm) {
_list.push(jm[i]);
}
Alternately, you could populate a separate array and then create a new List from that. In this case, however, you'll need to assign that new List.dataSource to the ListView in code:
var jm = json.return.markets;
var markets = [];
for (var i in jm) {
markets.push(jm[i]);
}
_list = new WinJS.Binding.List(markets);
var listview = document.getElementById("listView").winControl;
listview.itemDataSource = _list.dataSource;
Both ways will work (I tested them). Although the first solution is simpler and shorter, you'll need to make sure to clear out the List if you make another HTTP request and repopulate from that. With the second solution you just create a new List with each request and hand that to the ListView, which might work better depending on your particular needs.
Note also that in the second solution you can remove the itemDataSource option from the HTML altogether, and also eliminate the DataExample namespace and its variables because you'll assign the data source in code each time. Then you can also keep _list entirely local to the HTTP request.
Hope that helps. If you want to know more about ListView intricacies, see Chapter 7 of my free ebook from MSPress, Programming Windows Store Apps with HTML, CSS, and JavaScript, Second Edition.

Looping through JSON with node.js

I have a JSON file which I need to iterate over, as shown below...
{
"device_id": "8020",
"data": [{
"Timestamp": "04-29-11 05:22:39 pm",
"Start_Value": 0.02,
"Abstract": 18.60,
"Editor": 65.20
}, {
"Timestamp": "04-29-11 04:22:39 pm",
"End_Value": 22.22,
"Text": 8.65,
"Common": 1.10,
"Editable": "true",
"Insert": 6.0
}]
}
The keys in data will not always be the same (i've just used examples, there are 20 different keys), and as such, I cannot set up my script to statically reference them to get the values.
Otherwise I could state
var value1 = json.data.Timestamp;
var value2 = json.data.Start_Value;
var value3 = json.data.Abstract;
etc
In the past i've used a simple foreach loop on the data node...
foreach ($json->data as $key => $val) {
switch($key) {
case 'Timestamp':
//do this;
case: 'Start_Value':
//do this
}
}
But don't want to block the script. Any ideas?
You can iterate through JavaScript objects this way:
for(var attributename in myobject){
console.log(attributename+": "+myobject[attributename]);
}
myobject could be your json.data
I would recommend taking advantage of the fact that nodeJS will always be ES5. Remember this isn't the browser folks you can depend on the language's implementation on being stable. That said I would recommend against ever using a for-in loop in nodeJS, unless you really want to do deep recursion up the prototype chain. For simple, traditional looping I would recommend making good use of Object.keys method, in ES5. If you view the following JSPerf test, especially if you use Chrome (since it has the same engine as nodeJS), you will get a rough idea of how much more performant using this method is than using a for-in loop (roughly 10 times faster). Here's a sample of the code:
var keys = Object.keys( obj );
for( var i = 0,length = keys.length; i < length; i++ ) {
obj[ keys[ i ] ];
}
You may also want to use hasOwnProperty in the loop.
for (var prop in obj) {
if (obj.hasOwnProperty(prop)) {
switch (prop) {
// obj[prop] has the value
}
}
}
node.js is single-threaded which means your script will block whether you want it or not. Remember that V8 (Google's Javascript engine that node.js uses) compiles Javascript into machine code which means that most basic operations are really fast and looping through an object with 100 keys would probably take a couple of nanoseconds?
However, if you do a lot more inside the loop and you don't want it to block right now, you could do something like this
switch (prop) {
case 'Timestamp':
setTimeout(function() { ... }, 5);
break;
case 'Start_Value':
setTimeout(function() { ... }, 10);
break;
}
If your loop is doing some very CPU intensive work, you will need to spawn a child process to do that work or use web workers.
If you want to avoid blocking, which is only necessary for very large loops, then wrap the contents of your loop in a function called like this: process.nextTick(function(){<contents of loop>}), which will defer execution until the next tick, giving an opportunity for pending calls from other asynchronous functions to be processed.
My most preferred way is,
var objectKeysArray = Object.keys(yourJsonObj)
objectKeysArray.forEach(function(objKey) {
var objValue = yourJsonObj[objKey]
})
If we are using nodeJS, we should definitely take advantage of different libraries it provides. Inbuilt functions like each(), map(), reduce() and many more from underscoreJS reduces our efforts. Here's a sample
var _=require("underscore");
var fs=require("fs");
var jsonObject=JSON.parse(fs.readFileSync('YourJson.json', 'utf8'));
_.map( jsonObject, function(content) {
_.map(content,function(data){
if(data.Timestamp)
console.log(data.Timestamp)
})
})
A little late but I believe some further clarification is given below.
You can iterate through a JSON array with a simple loop as well, like:
for(var i = 0; i < jsonArray.length; i++)
{
console.log(jsonArray[i].attributename);
}
If you have a JSON object and you want to loop through all of its inner objects, then you first need to get all the keys in an array and loop through the keys to retrieve objects using the key names, like:
var keys = Object.keys(jsonObject);
for(var i = 0; i < keys.length; i++)
{
var key = keys[i];
console.log(jsonObject.key.attributename);
}
Not sure if it helps, but it looks like there might be a library for async iteration in node hosted here:https://github.com/caolan/async
Async is a utility module which provides straight-forward, powerful functions for working with asynchronous JavaScript. Although originally designed for use with node.js, it can also be used directly in the browser.
Async provides around 20 functions that include the usual 'functional' suspects (map, reduce, filter, forEach…) as well as some common patterns for asynchronous control flow (parallel, series, waterfall…). All these functions assume you follow the node.js convention of providing a single callback as the last argument of your async function.
Take a look at Traverse. It will recursively walk an object tree for you and at every node you have a number of different objects you can access - key of current node, value of current node, parent of current node, full key path of current node, etc. https://github.com/substack/js-traverse. I've used it to good effect on objects that I wanted to scrub circular references to and when I need to do a deep clone while transforming various data bits. Here's some code pulled form their samples to give you a flavor of what it can do.
var id = 54;
var callbacks = {};
var obj = { moo : function () {}, foo : [2,3,4, function () {}] };
var scrubbed = traverse(obj).map(function (x) {
if (typeof x === 'function') {
callbacks[id] = { id : id, f : x, path : this.path };
this.update('[Function]');
id++;
}
});
console.dir(scrubbed);
console.dir(callbacks);