Given
// tree data structure
Elephant --> Duck
--> Hamster --> Elephant1
--> Elephant2
Hamster --> Elephant --> Fish
--> Dog -------> Elephant
Dog --> Unicorn
--> Fish --> Hamster
--> Elephant --> Elephant
when user type term 'Ele', would like to output result so that all the tree is reduced to the following format (which has the matching result with its ancestor)
Elephant --> Hamster --> Elephant1
--> Elephant2
Hamster --> Elephant
--> Dog -------> Elephant
Dog --> Fish --> Elephant --> Elephant
Given
this.tree = [
{ id:1, name: 'Elephant',children:[
{ id:2, name: 'Duck' },
{ id:3, name: 'Hamster', children: [
{ id: 4, name: 'Elephant1', id: 5, name: 'Elephant2' }]
}
]},
{ id:5A, name: 'Hamster', children: [
{ id:6, name: 'Elephant', children: [
{ id:7, name: 'Fish' }
]},
{ id:8, name: 'Dog', children: [
{ id:9, name: 'Elephant' }
]}
]},
{ id:10, name: 'Dog', children: [
{ id:11, name: 'Unicorn' },
{ id:12, name: 'Fish', children: [
{ id:13, name: 'Hamster' },
{ id:14, name: 'Elephant', children:
[{ id:15, name: 'Elephant' }
]},
]}
]},
{ id:16, name: 'Elephant', children: [
{ id:17, name: 'Duck' },
{ id:18, name: 'Hamster', children: [
{ id:19, name: 'Elephant' },
{ id:20, name: 'Fish' }
]}
]}
]
My attempt:
I have tried the following using Depth First Search traversal but having issue to include its ancestor when its children has matching term. Currently the code only prints the matching result of the children but not its ancestor. I am half way through the solution (probably need some slight modification on my attempt solution) but is having trouble in the backtracking. Have been searching for resources on DFS and Would appreciate if anybody know about this.
onSearch(term) {
let found = this.search(term, JSON.parse(JSON.stringify(this.tree)));
console.log(found);
}
search(term, parent, path, basket) {
let fork = path.slice(0);
let found, { name, id, children } = parent;
let nameId = name + ':' +id;
fork.push(nameId);
if (name.toLowerCase().indexOf(term) !== -1) {
basket.push(fork);
fork = [];
return basket
}
if (!!children && children.length > 0) {
for (let child of parent.children) {
this.search(term, child, fork, basket)
}
return basket;
}
if (children.length === 0) {
return [];
}
}
this.onSearch('elep');
However the above attempt solution return the below result which is not exactly right.
Elephant --> Hamster --> Elephant1
Hamster --> Elephant
--> Dog -------> Elephant
Dog --> Fish --> Elephant
Desire solution is to output
Elephant --> Hamster --> Elephant1
--> Elephant2
Hamster --> Elephant
--> Dog -------> Elephant
Dog --> Fish --> Elephant --> Elephant
After few attempts, found the following solution which will output the desire solution
onSearch(term) {
let found = this.search(term, JSON.parse(JSON.stringify(this.tree)));
console.log(found);
}
search(term, parent, path, basket) {
let found, { name, id, children } = parent;
let nameId = name + ':' +id;
let fork = path.slice(0);
fork.push(nameId);
if (name.toLowerCase().indexOf(term) !== -1) {
basket.push([fork]);
}
if (!!children && children.length > 0) {
for (let child of parent.children) {
this.search(term, child, fork, basket)
}
return basket;
}
}
Related
I'm using cytoscape.js and when I try to initialize it, I found the canvas height to 0. I don't understand why.
This is my js :
var cy = cytoscape({container: document.getElementById("mapping")});
cy.add([
{group: "nodes", data: {id: "n0"}, position: {x:0, y:0}},
{group: "nodes", data: {id: "n1"}, position: {x:100, y:50}},
{group: "edges", data: {id: "e0", source: "n0", target: "n1"}}
]);
console.log(cy.container());
Here is the jsfiddle where you can see the "height":0px in log and nothing in rendered.
If you initialize cytoscape with static data, consider doing it like the documentation shows:
var cy = cytoscape({
container: document.getElementById('cy'), // container to render in
elements: [ // list of graph elements to start with
{ // node a
data: { id: 'a' }
},
{ // node b
data: { id: 'b' }
},
{ // edge ab
data: { id: 'ab', source: 'a', target: 'b' }
}
],
style: [ // the stylesheet for the graph
{
selector: 'node',
style: {
'background-color': '#666',
'label': 'data(id)'
}
},
{
selector: 'edge',
style: {
'width': 3,
'line-color': '#ccc',
'target-arrow-color': '#ccc',
'target-arrow-shape': 'triangle'
}
}
],
layout: { /!!!
name: 'grid',
rows: 1
}
});
you dont specify a layout in your sample code, i rarely do it like that, i simply add the nodes/edges and call the layout algorithm i want, the rest of your code seems ok, did you include all scripts and css files for cytoscape?
I've got the following scenario:
I load something from the database (meteor/mongo) e.g. with:
this.subscribe('cards', options, () => {
this.cards = Cards.find({}, {sort: this.sortObject.get()});
}, true);
In the template I iterate over those items using *ngFor="#card of cards"
When I extend the object on some action, e.g.
this.cards.forEach((card:Card) => {
card.distance = someValue;
});
the template doesn't get updated, i.e. when i use {{card.distance}}, it is not displayed.
Is it not possible to extend objects/lists subscribed to using meteor and angular 2?
This should work. Here is what I tried:
class Company {
constructor(private name:string) { }
}
#Component({
selector: 'contacts-list',
template: `
<ul>
<li *ngFor="#company of companies">{{company.name}} - {{company.distance}}</li>
</ul>
<div (click)="update()">Update</div>
<ul>
<li *ngFor="#company of rawCompanies">{{company.name}} - {{company.distance}}</li>
</ul>
<div (click)="updateRaw()">Update</div>
`
})
export class ContactsList {
constructor () {
this.companies = [
new Company('company 1'),
new Company('company 2'),
new Company('company 3'),
];
this.rawCompanies = [
{ name: 'company 1' },
{ name: 'company 2' },
{ name: 'company 3' }
];
}
update() {
this.companies.forEach((company:Company) => {
company.name = company.name + 'a';
company.distance = 'test';
});
}
updateRaw() {
this.rawCompanies.forEach((company) => {
company.name = company.name + 'a';
company.distance = 'test';
});
}
}
Both update and rawUpdate update the view... Is there something I missed?
See this plunkr: https://plnkr.co/edit/86i8RHYrFfRnTMMoQ1lH?p=preview.
<div class="section-form" ng-repeat="headings in QuestionsMain">
<p>{{headings.Title}}</p>
<ul>
<li ng-repeat="section in headings.FormSection">
<h3>{{ section.Title }}</h3>
<ul>
<li ng-repeat="group in section.FormGroups">
<div>{{ group.Title }}</div>
<div class="" ng-repeat="questions in group.FormQuestions">
<h4>{{ questions.QuestionName }} </h4>
<ul >
<li ng-repeat="answers in questions.FormAnswers">
<input type="radio" name="content" value="{{ answers.AnswerCode }}" id="{{ answers.FormAnswer_ID }}" />
<label for="{{ answers.FormAnswer_ID }}" class="radio-style"></label>
</li>
</ul>
</div>
Javascript
var eval = angular.module('evalQuestions', []);
//$scope.QuestionsMain contains json data
eval.controller('EvalController', ['$scope', 'questions', function ($scope, questions) {
questions.success(function (data) {
$scope.QuestionsMain = JSON.parse(data.d);
});
}]);
//calling webmethod for getting json data
eval.factory('questions', ['$http', function ($http) {
return ttp.post('EvaluationTest.aspx/GetEvaluationQuestionandAnswerDetails', { "evalNumber": 1, "languageCode": 'en' })
.success(function (data) {
return data;
})
.error(function (err) {
alert(err);
});
}]);
this is the json data received from my webmethod
[
{
"Title":"Test Title 1",
"FormSection":[
{
"ContentType":"GENR",
"Title":"Test section 1",
"FormGroups":[
{
"Title":"Test Group 1",,
"FormQuestions":[
{
"QuestionName":"rate your course?",
"QuestionCode":"100",
"FormAnswers":[
{
"AnswerName":"Excellent",
"AnswerCode":"5"
},
{
"AnswerName":"Very good",
"AnswerCode":"4"
},
{
"AnswerName":"Good",
"AnswerCode":"3"
},
{
"AnswerName":"Fair",
"AnswerCode":"2"
},
{
"AnswerName":"Poor",
"AnswerCode":"1"
}
]
},
{
"QuestionName":"rate your food?",
"QuestionCode":"200",
"FormAnswers":[
{
"AnswerName":"Excellent",
"AnswerCode":"5"
},
{
"AnswerName":"Very good",
"AnswerCode":"4",
},
{
"AnswerName":"Good",
"AnswerCode":"3",
},
{
"AnswerName":"Fair",
"AnswerCode":"2",
},
{
"AnswerName":"Poor",
"AnswerCode":"1",
}
]
}
]
}
]
},
{
"ContentType":"GENR2",
"Title":"Test section 2",
"FormGroups":[
{
"Title":"Test Group 2",,
"FormQuestions":[
{
"QuestionName":"rate your teacher?",
"QuestionCode":"300",
"FormAnswers":[
{
"AnswerName":"Excellent",
"AnswerCode":"5"
},
{
"AnswerName":"Very good",
"AnswerCode":"4"
},
{
"AnswerName":"Good",
"AnswerCode":"3"
},
{
"AnswerName":"Fair",
"AnswerCode":"2"
},
{
"AnswerName":"Poor",
"AnswerCode":"1"
}
]
},
{
"QuestionName":"rate your course content?",
"QuestionCode":"400",
"FormAnswers":[
{
"AnswerName":"Excellent",
"AnswerCode":"5"
},
{
"AnswerName":"Very good",
"AnswerCode":"4",
},
{
"AnswerName":"Good",
"AnswerCode":"3",
},
{
"AnswerName":"Fair",
"AnswerCode":"2",
},
{
"AnswerName":"Poor",
"AnswerCode":"1",
}
]
}
]
}
]
}
]
}
]
my asp.net page looks like the below
rate your teacher? 5 4 3 2 1 (these are radio buttons)
rate your class? 5 4 3 2 1 (these are radio buttons)
rate your food? 5 4 3 2 1 (these are radio buttons)
the html given above may not be proper but i think you can get the context of it.
I can bind the data properly. now i want to get questioncode and selected answercode and save the same to database on click of submit button.
is two way data binding possible here? please let me know one or more possible ways of achieving the same.
Your factory does not need to implement the success (if you want to do so, you have to return a promise (with the $q service)). imoo, in your case, using a service instead of a factory is best practice (services are singletons).
eval.service('questions', ['$http', function ($http) {
var service;
service.getEval = $http.post('EvaluationTest.aspx/GetEvaluationQuestionandAnswerDetails', { "evalNumber": 1, "languageCode": 'en' });
}]);
return service;
Then, inside your controller, you can implement the promise :
eval.controller('EvalController', ['$scope', 'questions', function ($scope, questions) {
questions.getEval().then(function (data) {
$scope.QuestionsMain = data;
}, function(error){
// Handle error here
});
$scope.save = function(){
// Call your question service here and save the object you will give him.
};
}]);
I think your view is correct (or almost). The 2-way data binding will be made automatically by angular.
edit :
to call your save function from the view, you just have to add the directive ng-click
eg :
<input type="button" ng-click="save()" value="submit" />
I'm learning about ember/ember-data and would like to fetch some data from server and order it.
My json data is something like
{
'ninjas': [
{ id: '1', name: 'Ninja 1', age: 23},
{ id: '2', name: 'Ninja 2', age: 27},
{ id: '3', name: 'Ninja 3', age: 22}
],
'clans': [
{ id: '1566', title: 'Foot Clan', ninja_ids: ['1', '2']},
{ id: '8941', title: 'Hand Clan', ninja_ids: ['3']}
]
}
The templates are
<script type="text/x-handlebars" data-template-name="clans">
<div>Clans</div>
<ul>
{{#each controller}}
<li>
{{#link-to 'clan' this}}{{title}}{{/link-to}}
</li>
{{/each}}
</ul>
</script>
<script type="text/x-handlebars" data-template-name="clan">
<div>{{title}}</div>
{{#each fetchedNinjas}}
<ul>
<li>{{fetchedNinjas}}</li>
</ul>
{{/each}}
</script>
Here is the basic App script:
var App = Ember.Application.create();
App.AplicationStore = DS.Store.extend({
revision: 12,
adapter: DS.RESTAdapter.create({})
});
App.Router.map(function() {
this.resource('ninjas');
this.resource('clans');
this.resource('clan', {path: 'clans/:clan_id'});
});
The ninja script is here:
App.Ninja = DS.Model.extend({
name: DS.attr('string'),
age: DS.attr('number'),
clans: DS.hasMany('clan')
});
App.NinjasRoute = Ember.Route.extend({
model: function (params, transition, queryParams) {
return this.store.find('ninja');
}
});
Here is the Clan Model, ClansRoute, ClanRoute
App.Clan = DS.Model.extend({
title: DS.attr('string'),
ninjas: DS.hasMany('ninja', {async: true})
});
App.ClansRoute = Ember.Route.extend({
model: function (params, transition, queryParams) {
return this.store.find('clan');
}
});
App.ClanRoute = Ember.Route.extend({
model: function (params, transition, queryParams) {
return this.store.find('clan', params.clan_id);
}
});
I think that I should get the related ninja data on ClanController and then order it, but I don't know how to proceed.
App.ClanController = Ember.ObjectController.extend({
fetchedNinjas: function () {
//should I use a promise here?
}.property('ninjas')
});
So i'm trying to filter my ng-repeat directive by using angular unique module.. but i'm kinda lost, i followed the exact order with adding the scripts... In console i'm getting an error, 'No module: ui.utils'..
Here is my Plunker: http://plnkr.co/edit/cKJgtGyYeQdQCyARXG7j?p=preview
From this code:
<ul>
<li>nokia</li>
<li>nokia</li>
<li>samsung</li>
</ul>
i should get with -unique- this code:
<ul>
<li>nokia</li>
<li>samsung</li>
</ul>
Some good people here helped me already by using jQuery filter, but i think this is much better way of doing this..
Any help is much appreciated..
You can use 'unique'(aliases: uniq) filter in angular.filter module (https://github.com/a8m/angular-filter)
usage: colection | uniq: 'property'
you can filter by nested properties to : colection | uniq: 'property.nested_property'
So you can do something like that..
function MainController ($scope) {
$scope.orders = [
{ id:1, customer: { name: 'foo', id: 10 } },
{ id:2, customer: { name: 'bar', id: 20 } },
{ id:3, customer: { name: 'foo', id: 10 } },
{ id:4, customer: { name: 'bar', id: 20 } },
{ id:5, customer: { name: 'baz', id: 30 } },
];
}
HTML: We filters by customer id, i.e remove duplicate customers
<th>All customers list: </th>
<tr ng-repeat="order in orders | unique: 'customer.id'" >
<td> {{ order.customer.name }} , {{ order.customer.id }} </td>
</tr>
result:
All customers list:
foo 10
bar 20
baz 30