Traversing an array tree? - javascript

My menu renders the "selected" array as options. Then when an item is selected it renders it's branches as the new options.
To keep track of traversal I create an array called select. So if someone picked the 3rd option then the 1st option then the 6th option, select = [3,1,6]
That's easy enough just pushing the index into the array, my question is how can I use this array to create a reference to the tree?
If select is [3,1,6] I want create a function that results in a reference to tree[3][1][6] also allowing me to traverse backwards by clipping off the last value of the array.
(in coffeescript)
tree:
name: 'name1'
branches:[
name: 'name2'
branches: [
name: 'name3'
branches: [
name: 'name4'
branches:[]
,
name: 'name5'
branches:[]
,
name: 'name6'
branches:[]
]
]
]
current = tree
#when clicked
$('.menu li').on 'click', ()->
select.push($(this).index())
for value in select
current = current+'['+value+']'
#this results in a string, not an actual reference to the tree.

If I understand correctly what you need, changing the last 2 lines with the following should do the trick:
current = tree
for value in select
current = current['branches'][value]

Related

How can we compare objects by value stored inside two arrays of objects in JavaScript?

I have one array of objects containing the menu for my multi select dropdown
const menu = [
{
label: "test 1",
value: "test1",
},
{
label: "test 2",
value: "test2",
},
// so on
]
I have another array of object that is storing selected multiple values from the menu:
const values = [
{
label: "test 1",
value: "test1",
}
// It can contain more than one values since the dropdown is multiselect
]
Remember that menu array contains all the options that I am displaying in my dropdown. Now, what I want is to filter out the selected values from the dropdown. For this purpose, I will need to filter out info from menu array of objects and for that filtration, I will be using values array of objects so that the objects inside the two arrays can be compared and filter out from my menu array of objects.
I have tried numerous JS methods but nothing is working in my case. I cannot use includes method because it compares values by reference and not by value. This is the reason why my condition fails since JS considers both of the objects to be different (their address is different), although they have the same value.
Looking forward to a solution.
selectedValues = values.map(item => item.value)
menu.filter(item => selectedValues.includes(item.value))

JQuery push array to specific element in array

I'm making a webapp with the help of JQuery to keep track of goals & habits.
One can add a main goal such as 'Discipline', and then afterwards can attach subgoals or habits to said main goal (e.g. 'work out everyday').
Organizing the array for main goals is obvious;
goals = ['Acceptance', 'Discipline', 'Accountability'];
I however have found no way in JQuery/Javascript to attach/add an array of items to a specific item in ANOTHER array.
Is there an easier way to do this, with JSON for example ?
Thanks in advance for any help offered
You do this by storing an array of objects at the top level, with a property for the child array
var goals = [{
name: "Acceptance",
children:[]
},{
name: "Discipline",
children:[]
},{
name: "Accountability",
children:[]
}];
When it comes to adding your child you just push it to the child array
goals[0].children.push("Work out every day");
Another option is store key/values at the top level
var goals = {"Acceptance":[],"Discipline":[],"Accountability":[]};
Slightly less versatile, but adding an item to a specific element slightly easier
goals["Acceptance"].push("Work out every day");

Can someone explain Angular as-for-in expression notation?

In Angular loops (ng-repeat, ng-options) you can use the following syntax:
item as item.label for item in items
Can someone please explain what each of the tokens in the expression is doing there and what it means? Can you point me to the documentation of this? I can't figure out what to search for (searching for 'as' or 'for' is useless). It is not mentioned in the documentation for ng-repeat or ng-options.
I know that somehow it lets you pick an object from a list of objects, but 'item' appears in the expression twice and it is not clear to me what the role of that token is in this expression.
Sorry if this is all documented some place which I can not find....
You have an array "items". And you are interating through it with
item in items
As the example you have copied incompletely from this page "https://docs.angularjs.org/api/ng/directive/ngOptions" would normally create a dropdown, there would be now the problem that the "item" object you currently have in your iteration has more fields than just a string to show as label for your dropdown entry. Here is the object again:
$scope.items = [{
id: 1,
label: 'aLabel',
subItem: { name: 'aSubItem' }
}, {
id: 2,
label: 'bLabel',
subItem: { name: 'bSubItem' }
}];
So what do you want to show then? Yeah, you want to show "item.label".
And thats what
item as item.label
does. It tells the loop to use the current "item.label" value as "item" for this specific loop.

AngularJS Select box not updating when model is changed

So I have a controller like this
app.controller("ReservationCtrl", function($scope) {
$scope.rooms = [{id: 1, name: "Room 1"}, {id: 2, name: "Room 2", }];
$scope.reservation = {id: 1, time: "9AM", room: {id: 2, name: "Room 2"}};
});
A view that looks like this:
<select ng-model="reservation.room" ng-options="room as room.name for room in rooms"></select>
The problem is that the select box won't bind to the correct room unless I say
$scope.reservation.room = $scope.rooms[1];
This is rather inconvenient for development as the room is not the only field on the reservation model that needs to be bound to a select box. How can I apply the binding without doing this extra step?
Also, the binding breaks again if I do something like
$http.get("/reservation/2").success(function(data) { $scope.reservation = angular.copy(data); });
I think this is because,
in your ng-options if you select an option, the selected value is an object
EX: if you select first option then model value is an object which is the first element in the $scope.rooms ({id: 1, name: "Room 1"}).
objects are reference type data type. So what it does is if the objects create one time then all of its usage are pointing to that object. have a look at this article.
So your selected value is an object which is a reference type. In your case you have two independent objects for the $scope.reservation.room and the {id: 2, name: "Room 2"} which is in rooms array. note that they are in separated memory slots.
In your working case both of the $scope.reservation.room and the {id: 2, name: "Room 2"} which is in rooms array are pointing to same memory slot because you have equals the two as $scope.reservation.room = $scope.rooms[1]; This means $scope.reservation.room & $scope.rooms[1] both are pointing to the same object in the memory.
To work this out you can do something different than your working solution.
change the ng-repeat as following
...ng-options="room.id as room.name for room in rooms"...
and change the ng-model
....ng-model="reservation.id"....
this will select the id of the option as the selected value for EX if you select the first option then the model value will be 1 which is the id of the first option.
in this case selected model values are primitive(like 1,2,3..) type data then its not going to search for the objects in memory instead it will get the value of stack and check with the option values and select the correct one.
here is a DEMO this will select the second option initially.
------------------------------------------------- SUMMARY -------------------------------------------------
If the selected model value is an object then it will check the memory address of the selected object and the all objects of the $scope.rooms and check if there is a matching element and select the matching option if one is found. if no one found then nothing will select.
If the model values are primitive like 1,2,3.. it will search the value and check if there any matching option value if one is found it will select that option.
So it seems the key to this problem is the track by clause in the ng-options directive. See my updated fiddle
As you will see, the whole model is being updated and not just its ID. The documentation says that select as and track by were not meant to be used together, but the example they used to illustrate this is a bit different from mine.
Since I've received no feedback from the official Angular channels on this matter to date, I'm gonna mark this as solved and move on.
Thanks for the help everyone.
When the view is loaded, the select box has no selected value because the room object in $scope.reservation is not the same object as the one in $scope.rooms, event if it has the same values.
Thats why your example does not work (fiddle)
But this one works:
$scope.rooms = [{id: 1, name: "Room 1"}, {id: 2, name: "Room 2"}];
$scope.reservation = {
id: 1,
time: "9AM",
room: $scope.rooms[1] // <-- now the reservation room references a valid ng-repeated room
};
See updated fiddle
To avoid problems, I'll suggest you bind to the variable of your select box only the room id of the reservation. Because it is a primitive type, the comparison will be made by value, and that will solve also your second use case. Moreover, you better not duplicate data in the reservation object. See fiddle
If you need to display the name of the reserved (selected) room, you could easily write a getRoomById function that will look into the array of rooms.
Change the scope only when the value changes.
if($scope.reservation.room != $scope.rooms[1])
$scope.reservation.room = $scope.rooms[1];

In Ember data, what is the best way to form association between two objects?

Let's say some DS.Model class Leaf belongsTo class Tree, and one Tree hasMany leaves, ie:
App.Leaf = DS.Model.extend({
tree: DS.belongsTo('App.Tree'),
});
App.Tree = DS.Model.extend({
leaves: DS.hasMany('App.Leaf'),
})
So far I'm manually manipulating Tree's leaves field:
tree = App.store.create( App.Tree )
leaf = App.store.create( App.Leaf )
tree.get('leaves').pushObject( leaf )
App.store.commit()
Now this appears to work but then things get weird:
when I check leaf's tree field, I see a App.Tree instance is in there and the id matchs that of tree:
leaf.get('tree').get('id') // outputs 1
tree.get('id') // outputs 1
So far ok. Now I check tree's leaves field, which I presume is an Ember mutable-array, and I see this:
branch.get('leaves').content // outputs [ 2 ]
leaf.get('id') // outputs 1
So I presume the leaves mutable-array is storing an array of leaf ids, except its id does not match that of the leaf instance.
Note when the leaf's id is 2, it's stored in the branch.leaves.content field as 4, if leaf id is 3, the stored id is 6, etc.
Everything is working properly in your example, accessing the content variable branch.get('leaves') record array returns an array of the clientId's of the objects.
However that is the exception as accessing properties any other way will transparently accesses the objects themselves.
In your case if you want ID's use branch.get('leaves').mapProperty('id')

Categories