ImmutableJs - compare objects but for one property - immutable.js

I am converting a shopping basket to an immutable structure.
Is there an easy way with immutablejs to see if an immutable object already exists within an immutable list EXCEPT for one object property 'quantity' which could be different? List example:
[{
id: 1,
name: 'fish and chips',
modifiers: [
{
id: 'mod1',
name: 'Extra chips'
}
],
quantity: 2
},{
id: 2,
name: 'burger and chips',
modifiers: [
{
id: 'mod1',
name: 'No salad'
}
],
quantity: 1
}]
Now, say I had another object to put in the list. But I want to check if this exact item with modifiers exists in the list already? I could just do list.findIndex(item => item === newItem) but because of the possible different quantity property then it wont work. Is there a way to === check apart from one property? Or any way to do this without having to loop through every property (aside from quantity) to see if they are the same?
Currently, I have an awful nested loop to go through every item and check every property to see if it is the same.

Well this should work-
list.findIndex(item => item.delete("quantity").equals(newItem.delete("quantity"))
The equals method does deep value comparison. So once you delete the quantity, you are comparing all values that matter.
PS: please ignore code formatting, I am on SO app.
PPS: the above code is not optimal, you should compare a pre-trimmed newItem inside the arrow function instead of trimming it there.

Related

DC.JS How to handle objects with different amount of properties

Let's say i have 2 objects each with the same properties but one has an extra property middleName and the other does not.
How should i handle this in DC.js?
var objects = [{
name: "De Smet",
firstName: "Jasper",
adress: "Borsbeke",
},{
name: "De Backer",
firstName: "Dieter",
middleName: "middleName",
adress: "Borsbeke"
},{
name: "De Bondtr",
firstName: "Simon",
middleName: "OtherMiddleName",
adress: "Denderleeuw"
}
]
The wanted behaviour would be that the object without the property gets filtered out. Like so:
Here is a fiddle:
https://jsfiddle.net/mj92shru/41/
It seems to add the property middlename to the first object and assigns it the next middlename it finds
Adding the property to the first object and adding a placeholder value like "none" works but it doesnt really produce wanted behaviour.
I realize i could filter out the objects where the middlename is set to "none" but this would be difficult in the actual application i am writing
i've also found that adding the object without the property last causes it to crash.
Indeed, using undefined fields for your dimension or group keys can crash crossfilter because it does not validate its data. NaN, null, and undefined do not have well-defined sorting operations.
It's strange to see the value folded into another bin, but I suspect it's the same undefined behavior, rather than something you can depend on.
If you have fields which may be undefined, you should always default them, even if you don't want the value:
middleNameDimension = j.dimension(d => d.middleName || 'foo'),
I think you do want to filter your data, but not in the crossfilter sense where those rows are removed and do not influence other charts. Instead, it should just be removed from the group without affecting anything else.
You can use a "fake group" for this, and there is one in the FAQ which is suited perfectly for your problem:
function remove_bins(source_group) { // (source_group, bins...}
var bins = Array.prototype.slice.call(arguments, 1);
return {
all:function () {
return source_group.all().filter(function(d) {
return bins.indexOf(d.key) === -1;
});
}
};
}
Apply it like this:
.group(remove_bins(middleNameGroup, 'foo'))
Fork of your fiddle.
Be careful with this, because a pie chart implicitly adds up to 100%, and in this case it only adds up to 66%. This may be confusing for users, depending how it is used.

Schema JSON - Get selected item from drop down list to populate corresponding data into a text box

Using Schema JSON, I have this drop down list that has data populated into it. Each item in the drop down list has a value. How do I get that value and populate data into a different text box?
For example, let's say the drop down looks like:
A
B
C
D
If the user selects "A", how do I find that the user selected "A"? Or even get the selected value 1? And then getting the selected value, a textbox under will say "Correct" or "Incorrect".
Here's what I'm working with:
vm.sf.form = [
{
type: 'fieldset',
title: 'Some Title',
items: [
{ key: "Answers", title: "Choose One", type: 'select', onChange: "itemSelected(form.titleMap)", titleMap: getTitleMap()},
{ key: "isCorrect" title: "Your Answer is: ", type: 'string', readonly: true}
]
}];
The "onChange: "itemSelected(form.titleMap)" calls a function itemSelected, which is where I would like to have the functionality check what is selected.
The form.titleMap returns "A, B, C, D" but not the actual selected value.
The function for itemSelected looks like this:
$scope.itemSelected = function (value) {
var answer = value;
if (answer == 'A'){
//set isCorrect to say "Correct" in the text box...
}
}
The itemSelected function just test code for now..
In this function, "value" returns "A, B, C, D" instead of the actual selected value, since I am passing in the titleMap. How do I pass in the selected value? And then how do I change the textbox text to say "Correct" or "Incorrect"?
I figured it out when I changed the form.titleMap to "this". It helped me map around the form to find the actual value that was selected. When I found the actual selected value, it was easy to do the rest.
I hope this helps others that may have the same issue...

Prevent change of 'copied' object affecting original using ng-repeat in angular

I have a setup in angular which displays a json string called 'items'. Each item contains an array of field ids. By matching the field ids, it pulls information for the fields using a seperate 'fields' json string.
{
"data": [
{
"id": "1",
"title": "Item 1",
"fields": [
1,
1
]
},
{
"id": "2",
"title": "Item 2",
"fields": [
1,
3
]
},
{
"id": 3,
"title": "fsdfs"
}
]
}
You can copy or delete either the items or fields, which will modify the 'items' json.
Everything works except when I copy one item (and its fields), and then choose to delete a field for that specific item.
What happens is that it deletes the field for both the copied item AND the original.
Plunker -
http://plnkr.co/edit/hN8tQiBMBhQ1jwmPiZp3?p=preview
I've read that using 'track by' helps to index each item as unique and prevent duplicate keys, but this seems to be having no effect.
Any help appreciated, thanks
Edit -
Credit to Eric McCormick for this one, using angular.copy with array.push solved this issue.
Instead of -
$scope.copyItem = function (index) {
items.data.push({
id: $scope.items.data.length + 1,
title: items.data[index].title,
fields: items.data[index].fields
});
}
This worked -
$scope.copyItem = function (index) {
items.data.push(angular.copy(items.data[index]));
}
I recommend using angular.copy, which is a "deep copy" of the source object. This is a unique object from the source one.
It may seem slightly counter-intuitive, but a direct reference (as you're observing) interacts with the original object. If you inspect the element's scope after it's instantiated in the DOM, you can see there's a $id property assigned to the object in memory. Basically, by using angular.copy(source, destination), you ensure a copying of all the properties/values and having a unique object.
Example:
//inside the controller, a function to instantiate new copy of selected object
this.selectItem = function(item){
var copyOfItem = angular.copy(item);
//new item with same properties and values but unique object!
}
Egghead.io has a video on angular.copy.

CSV Parser through angularJS

I am building a CSV file parser through node and Angular . so basically a user upload a csv file , on my server side which is node the csv file is traversed and parsed using node-csv
. This works fine and it returns me an array of object based on csv file given as input , Now on angular end I need to display two table one is csv file data itself and another is cross tabulation analysis. I am facing problem while rendering data, so for a table like
I am getting parse responce as
For cross tabulation we need data in a tabular form as
I have a object array which I need to manipulate in best possible way so as to make easily render on html page . I am not getting a way how to do calculation on data I get so as to store cross tabulation result .Any idea on how should I approach .
data json is :
[{"Sample #":"1","Gender":"Female","Handedness;":"Right-handed;"},{"Sample #":"2","Gender":"Male","Handedness;":"Left-handed;"},{"Sample #":"3","Gender":"Female","Handedness;":"Right-handed;"},{"Sample #":"4","Gender":"Male","Handedness;":"Right-handed;"},{"Sample #":"5","Gender":"Male","Handedness;":"Left-handed;"},{"Sample #":"6","Gender":"Male","Handedness;":"Right-handed;"},{"Sample #":"7","Gender":"Female","Handedness;":"Right-handed;"},{"Sample #":"8","Gender":"Female","Handedness;":"Left-handed;"},{"Sample #":"9","Gender":"Male","Handedness;":"Right-handed;"},{"Sample #":";"}
There are many ways you can do this and since you have not been very specific on the usage, I will go with the simplest one.
Assuming you have an object structure such as this:
[
{gender: 'female', handdness: 'lefthanded', id: 1},
{gender: 'male', handdness: 'lefthanded', id: 2},
{gender: 'female', handdness: 'righthanded', id: 3},
{gender: 'female', handdness: 'lefthanded', id: 4},
{gender: 'female', handdness: 'righthanded', id: 5}
]
and in your controller you have exposed this with something like:
$scope.members = [the above array of objects];
and you want to display the total of female members of this object, you could filter this in your html
{{(members | filter:{gender:'female'}).length}}
Now, if you are going to make this a table it will obviously make some ugly and unreadable html so especially if you are going to repeat using this, it would be a good case for making a directive and repeat it anywhere, with the prerequisite of providing a scope object named tabData (or whatever you wish) in your parent scope
.directive('tabbed', function () {
return {
restrict: 'E',
template: '<table><tr><td>{{(tabData | filter:{gender:"female"}).length}}</td></tr><td>{{(tabData | filter:{handedness:"lefthanded"}).length}}</td></table>'
}
});
You would use this in your html like so:
<tabbed></tabbed>
And there are ofcourse many ways to improve this as you wish.
This is more of a general data structure/JS question than Angular related.
Functional helpers from Lo-dash come in very handy here:
_(data) // Create a chainable object from the data to execute functions with
.groupBy('Gender') // Group the data by its `Gender` attribute
// map these groups, using `mapValues` so the named `Gender` keys persist
.mapValues(function(gender) {
// Create named count objects for all handednesses
var counts = _.countBy(gender, 'Handedness');
// Calculate the total of all handednesses by summing
// all the values of this named object
counts.Total = _(counts)
.values()
.reduce(function(sum, num) { return sum + num });
// Return this named count object -- this is what each gender will map to
return counts;
}).value(); // get the value of the chain
No need to worry about for-loops or anything of the sort, and this code also works without any changes for more than two genders (even for more than two handednesses - think of the aliens and the ambidextrous). If you aren't sure exactly what's happening, it should be easy enough to pick apart the single steps and their result values of this code example.
Calculating the total row for all genders will work in a similar manner.

I can't manage to create 3rd level of dijit.Tree

I wanted to create a 3 level dijit.Tree, like that:
-root
|
--level1
|
--level2
I thought it would be really simple since there's a code snippet in this tutorial (example 1). But somehow I manage to fail.
This is my dojo code (variable names are in Polish, I hope it's not a problem):
modelRaportow = new dijit.tree.ForestStoreModel({
store: new dojo.data.ItemFileReadStore({
url: "logika/getJSON/getStatusRaportow.php"
}),
query: {typ: 'galaz'},
rootId: 'statusRaportuRoot',
rootLabel: 'Status raportu',
childrenAttrs: 'raporty'
});
drzewoRaportow = new dijit.Tree({
openOnClick: true,
model: modelRaportow,
showRoot: true,
persist: false
}, "target-status-raportow");
drzewoRaportow.startup();
This is my JSON returned by logika/getJSON/getStatusRaportow.php (again, names are in Polish):
{
"identifier":"id",
"label":"status",
"items": [
{"id":0,"status":"zaakceptowane","typ":"galaz"
"raporty":[{"_reference":1},{"_reference":2},{"_reference":3}]},
{"id":1,"data":"24-10-2011","wykonujacy":"cblajszczak","idKlienta":3,"status":"Raport0","typ":"lisc"},
{"id":2,"data":"24-10-2011","wykonujacy":"cblajszczak","idKlienta":1,"status":"Raport1","typ":"lisc"},
{"id":3,"data":"24-10-2011","wykonujacy":"cblajszczak","idKlienta":3,"status":"Raport2","typ":"lisc"},
{"id":4,"status":"odrzucone","typ":"galaz"
"raporty":[{"_reference":5},{"_reference":6},{"_reference":7}]},
{"id":5,"data":"24-10-2011","wykonujacy":"cblajszczak","idKlienta":1,"status":"Raport3","typ":"lisc"},
{"id":6,"data":"24-10-2011","wykonujacy":"cblajszczak","idKlienta":3,"status":"Raport4","typ":"lisc"},
{"id":7,"data":"24-10-2011","wykonujacy":"cblajszczak","idKlienta":3,"status":"Raport5","typ":"lisc"}
]}
And finally, this is what I'm getting: img - root node and lvl 1 nodes returned by query, no child nodes.
The question is - where is my mistake? Can anyone see it?
You have no comma between the typ and raporty value pair.
I have a partial answer: by stepping through the code in a similar situation, I've discovered that it expects childrenAttrs to be an array, so it should be:
childrenAttrs: ['raporty']
but I still cannot get the third level to appear in my case.