Angular nested filters by checkbox - javascript

I'm trying to build a small schedule app, which displays events happening on one of two days. Users are currently able to filter the category/topic of the events by checkboxes.
Here is a demo: http://jsfiddle.net/qx3cD/201/
I want to create a nested filter, that first allows users to choose either Day 1 or Day 2, and then filters through those results by category.
Is there a way to perform a nested filter with Angular?
JS
function MyCtrl($scope) {
$scope.showAll = true;
$scope.checkChange = function() {
for(t in $scope.categoryArray){
if($scope.categoryArray[t].on){
$scope.showAll = false;
return;
}
}
$scope.showAll = true;
};
$scope.myFunc = function(a) {
if($scope.showAll) { return true; }
var sel = false;
for(cat in $scope.categoryArray){
var t = $scope.categoryArray[cat];
console.log(t);
if(t.on){
if(a.category.indexOf(t.name) == -1){
return false;
}else{
sel = true;
}
}
}
return sel;
};
$scope.categoryArray = [{ name: "A", on: false}, {name:"B", on: false}, {name:"C", on: false}, {name:"D", on: false}, {name:"E", on: false}, {name:"F", on: false}, {name:"G", on: false}, {name:"H", on: false}];
$scope.sessionArray = [{
"time": "9:00",
"day": "day 1",
"name": "Session One",
"category": ["A", "B", "E", "D"]
}, {
"time": "10:00",
"day": "day 1",
"name": "Session Two",
"category": ["A", "B", "C", "D"]
}, {
"time": "11:00",
"day": "day 1",
"name": "Session Three",
"category": ["G", "F", "D", "E"]
}, {
"time": "12:00",
"day": "day 1",
"name": "Intermission A",
"category": ["A", "B", "C", "D"]
}, {
"time": "13:00",
"day": "day 1",
"name": "Session Four",
"category": ["H", "A", "E"]
}, {
"time": "9:00",
"day": "day 2",
"name": "Session Five",
"category": ["D", "E", "B", "G"]
}, {
"time": "11:00",
"day": "day 2",
"name": "Session Six",
"category": ["A", "E", "C"]
}, {
"time": "12:00",
"day": "day 2",
"name": "Session Seven",
"category": ["G", "H", "B", "C"]
}]
HTML
<li ng-repeat="cat in categoryArray">
<label>
<input type="checkbox" ng-model="cat.on" ng-change="checkChange()" />{{cat.name}}</label>
</li>
<hr>
<h1><strong>Category:</strong></h1>
<div ng-repeat="sessionItem in sessionArray | filter:myFunc | orderBy: 'id'" class="ng-scope">
<h3>{{sessionItem.name}}</h3>
</div>
DEMO

To nest another filter just add another pipe | and use the "filter" filter normally.
<div ng-repeat="sessionItem in sessionArray | filter:myFunc | filter:theFilter | orderBy: 'id'">
Check the EXAMPLE here.

Related

Search value in object of various data in Javascript recursivly

Good day everyone! I have a problem with a search function on JavaScript.
This is an object I have (states):
{
"1": {
"id": "1",
"name": "Category #1",
"hasChild": "Y",
"count": "0",
"parentId": null,
"link": "/catalog/",
"subcategories": [
{
"id": "21",
"name": "Subcategory #1",
"hasChild": "Y",
"count": "0",
"parentId": "1",
"link": "/catalog/",
"subcategories": [
{
"id": "24",
"name": "subsubcategory #1",
"hasChild": "Y",
"count": "1",
"parentId": "21",
"link": "/catalog/",
"subcategories": [],
},
{
"id": "25",
"name": "subsubcategory #2",
"hasChild": "Y",
"count": "0",
"parentId": "21",
"link": "/catalog/",
"subcategories": [],
}
],
},
{
"id": "22",
"name": "Subcategory #2",
"hasChild": "Y",
"count": "0",
"parentId": "1",
"link": "/catalog/",
},
{
"id": "23",
"name": "Subcategory #3",
"hasChild": "Y",
"count": "0",
"parentId": "1",
"link": "/catalog/",
}
],
},
"2": {
"id": "2",
"name": "Category #2",
"hasChild": "Y",
"count": "0",
"parentId": null,
"link": "/catalog/",
"subcategories": [
..
],
},
}
And I have an array of products to which one has an id of the category to which it belongs. So I extracted from there only unique values of categories. It can be any level.
["24", "22", "2" ...]
My goal is to take the "name" values of parents' categories.
Example: product is in a category with id:24 (name: subsubcategory #1).
How can I get the value "Category #1" from the top category?
I use that function, but it only work for me on 1-st level (if id:1 or 2)
function filter(item, search, textKey) {
let result = []
const _filter = (item, search, textKey) => {
for (const i of item) {
if (i[textKey].indexOf(search) !== -1) {
result = [...result, { name: i.name, id: i.id, parentId: i.parentId }]
}
i.children ? _filter(i.children, search, textKey) : null
}
}
_filter(item, search, textKey)
return result
}
console.log(filter(Object.values(states), '24', 'id')) // didn't work
console.log(filter(Object.values(states), '2', 'id')) // found and mapped
I would build a function that finds the names associated with a list of ids by mapping over a function that finds the names associated with a single id. That I would build atop a function which recursively finds the full ancestor node based on an arbitrary predicate.
Your initial input is not quite a recursive structure. It looks like it might be a bad copy from console output, although it could still be legitimate. In any case, we do a transformation first, using Object .values to extract an array of actual category nodes. This could be moved from one level of the call chain to another, depending on what level of reuse you want from these functions. If your data is in fact an array, this will still work, but I would suggest replacing Object .values (states) with just states, as it makes for cleaner code.
const rootAncestor = (pred) => (xs) =>
xs .find (x => pred (x) || rootAncestor (pred) (x .subcategories || []))
const rootAncestorName = (states) => (id) =>
rootAncestor (x => x .id == id) (states) ?.name ?? ''
const rootAncestorNames = (states) => (ids) =>
ids .map (rootAncestorName (Object .values (states)))
const states = {1: {id: "1", name: "Category #1", hasChild: "Y", count: "0", parentId: null, link: "/catalog/", subcategories: [{id: "21", name: "Subcategory #1", hasChild: "Y", count: "0", parentId: "1", link: "/catalog/", subcategories: [{id: "24", name: "subsubcategory #1", hasChild: "Y", count: "1", parentId: "21", link: "/catalog/", subcategories: []}, {id: "25", name: "subsubcategory #2", hasChild: "Y", count: "0", parentId: "21", link: "/catalog/", subcategories: []}]}, {id: "22", name: "Subcategory #2", hasChild: "Y", count: "0", parentId: "1", link: "/catalog/"}, {id: "23", name: "Subcategory #3", hasChild: "Y", count: "0", parentId: "1", link: "/catalog/"}]}, 2: {id: "2", name: "Category #2", hasChild: "Y", count: "0", parentId: null, link: "/catalog/", subcategories: []}}
console .log (rootAncestorNames (states) (['24', '22', '2', '42']))
I added a failing lookup with 42 to show that I make the guess that we want to return an empty string. But at the end of rootAncestorName, you could replace that with null, undefined, or some other token.

how to transform array of object using reduce of map?

I am trying to transform array of object to different format.I also take reference from this url
Group js objects by multiple properties
But not able to solve the problem
I have a input array of object which have class and sections I need to transform or filter student in different format on basis of class and section.
here is my code
https://jsbin.com/nidudisuza/edit?js,output
let expectedout = [
{
class: 1,
sections: [{
"section": "B",
students: [ {
"name": "Test",
"class": 1,
"gender": "M",
"section": "B",
"rollNumber": "123111",
"sports": [
"Badminton",
"Chess"
],
"age": 7
}]
}]
},
{
class: 3,
sections: [{
"section": "B",
students: [{
"name": "Rahul",
"class": 3,
"gender": "M",
"section": "B",
"rollNumber": "1231",
"sports": [
"Badminton",
"Chess"
],
"age": 7
}]
}]
},
{
class: 5,
sections: [{
"section": "C",
students: [
{
"name": "Rajat",
"class": 5,
"gender": "M",
"section": "C",
"rollNumber": "123122",
"sports": [
"Chess"
],
"age": 9
}
]
}]
}
]
const input = [{
"name": "Rahul",
"class": 3,
"gender": "M",
"section": "B",
"rollNumber": "1231",
"sports": [
"Badminton",
"Chess"
],
"age": 7
},
{
"name": "Rajat",
"class": 5,
"gender": "M",
"section": "C",
"rollNumber": "123122",
"sports": [
"Chess"
],
"age": 9
},
{
"name": "Test",
"class": 1,
"gender": "M",
"section": "B",
"rollNumber": "123111",
"sports": [
"Badminton",
"Chess"
],
"age": 7
},
]
function abc() {
// let output =
// data.map((i) => {
// let obj = {};
// obj.class = i.class;
// obj.sections = [];
// obj.sections.push({
// section:obj.section,
// students:[obj]
// })
// return obj;
// })
let output =input.reduce((acc,i)=>{
let cls = i.class;
const found = acc.some(el => el.class === cls);
let obj = {
section:i.section,
students:[]
}
found.sections.students.push[i]
},[])
return output
}
console.log(abc())
}
I'd go for a 2 step process:
Reorganize the values to be mapped by an index (both for class and sections)
Then flatten out everything in arrays; depending on what you're doing having a dictionary could also be convenient.
Below you can find the function that makes the dictionary, in the snippet there is also the second step that transforms the dictionary values in arrays.
function abc() {
let output = input.reduce((ac, x) => {
let keyCl = x.class
let keySec = x.section
let orgClass = ac[keyCl]
let sections = orgClass && orgClass.sections || {}
let oldSec = sections && sections[keySec]
let sectionBase = oldSec || {
section: keySec,
students: []
}
sectionBase.students = [...(oldSec ? .students || []), x]
return {
...ac,
// we organize classes to be indexed in an obj,
// we can flat this out later
[keyCl]: {
class: keyCl,
sections: {
...sections,
// like for classes we organize sections by key
[keySec]: sectionBase
}
}
}
}, {})
return output
}
let expectedout = [{
class: 1,
sections: [{
"section": "B",
students: [{
"name": "Test",
"class": 1,
"gender": "M",
"section": "B",
"rollNumber": "123111",
"sports": [
"Badminton",
"Chess"
],
"age": 7
}]
}]
},
{
class: 3,
sections: [{
"section": "B",
students: [{
"name": "Rahul",
"class": 3,
"gender": "M",
"section": "B",
"rollNumber": "1231",
"sports": [
"Badminton",
"Chess"
],
"age": 7
}]
}]
},
{
class: 5,
sections: [{
"section": "C",
students: [
{
"name": "Rajat",
"class": 5,
"gender": "M",
"section": "C",
"rollNumber": "123122",
"sports": [
"Chess"
],
"age": 9
}
]
}]
}
]
const input = [{
"name": "Rahul",
"class": 3,
"gender": "M",
"section": "B",
"rollNumber": "1231",
"sports": [
"Badminton",
"Chess"
],
"age": 7
},
{
"name": "Rajat",
"class": 5,
"gender": "M",
"section": "C",
"rollNumber": "123122",
"sports": [
"Chess"
],
"age": 9
},
{
"name": "Test",
"class": 1,
"gender": "M",
"section": "B",
"rollNumber": "123111",
"sports": [
"Badminton",
"Chess"
],
"age": 7
},
]
function abc() {
let output = input.reduce((ac, x) => {
let keyCl = x.class
let keySec = x.section
let orgClass = ac[keyCl]
let sections = orgClass && orgClass.sections || {}
let oldSec = sections && sections[keySec]
let sectionBase = oldSec || {
section: keySec,
students: []
}
sectionBase.students = [...(oldSec && oldSec.students || []), x]
return {
...ac,
// we organize classes to be indexed in an obj,
// we can flat this out later
[keyCl]: {
class: keyCl,
sections: {
...sections,
// like for classes we organize sections by key
[keySec]: sectionBase
}
}
}
}, {})
return output
}
let res = abc()
console.log('with keys', res)
let onlyValues = Object.values(res).map(x => ({ ...x,
sections: Object.values(x.sections)
}))
console.log('only values', onlyValues)
Using Array#reduce to build an Object with the collected data. Foreach object look in the accumulated object if there exists an prperty for this class. If not create one and add to this the groundstructure for this class.
Afterwards add in both cases to the students array a new entry for this student.
After creating by this the object use Object#entries to take only the values of the object to get the ished array.
function modify(data) {
let res= Object.values(data.reduce((acc, cur) => {
if (!acc[cur.class]) {
acc[cur.class] = {
class: cur.class,
sections: [{
"section": cur.section,
students: []
}]
};
}
acc[cur.class].sections[0].students.push(cur);
return acc;
},{}));
return res;
}
const data = [{
"name": "Rahul",
"class": 3,
"gender": "M",
"section": "B",
"rollNumber": "1231",
"sports": [
"Badminton",
"Chess"
],
"age": 7
},
{
"name": "Rajat",
"class": 5,
"gender": "M",
"section": "C",
"rollNumber": "123122",
"sports": [
"Chess"
],
"age": 9
},
{
"name": "Test",
"class": 1,
"gender": "M",
"section": "B",
"rollNumber": "123111",
"sports": [
"Badminton",
"Chess"
],
"age": 7
},
]
console.log(modify(data));

Better way to SUM previous to next value javascript

I have a below array:
[
{
"model": "A",
"count": "10",
"date": "02-12-2004"
},
{
"model": "B",
"count": "20",
"date": "02-01-2004"
},
{
"model": "C",
"count": "30",
"date": "02-02-2004"
}
]
I need a new array set to be like the following
[
{
"model": "A",
"count": "10",
"date": "02-12-2004"
},
{
"model": "B",
"count": "30",
"date": "02-01-2004"
},
{
"model": "C",
"count": "60",
"date": "02-02-2004"
}
]
It means that all previous set count should be added to the next set.
Try this.
arr=[{"model":"A", "count":"10","date":"02-12-2004"},{"model":"B", "count":"20","date":"02-01-2004"},{"model":"C", "count":"30","date":"02-02-2004"}]
count=0;
let arr2 = arr.filter(obj=>{
count+=parseInt(obj.count);
obj.count=count;
})
console.log(arr)
If you dont want to modify the existing array .Use Object.assign method like below
arr=[{"model":"A", "count":"10","date":"02-12-2004"},{"model":"B", "count":"20","date":"02-01-2004"},{"model":"C", "count":"30","date":"02-02-2004"}]
count=0;
let arr2 = arr.map(obj=>{
count += parseInt(obj.count);
return Object.assign({}, obj, {
count:count
});
});
console.log(arr2)

how to filter an array based on dynamic values in javascript

i want to filter the given below array on the basis of dynamic values (name and section).
var allResponse = [
{
"name": "ABC",
"class": "8",
"section": "A",
},
{
"name": "DEF",
"class": "7",
"section": "B",
},
{
"name": "ABC",
"class": "8",
"section": "D",
},
]
These values of name and section, i'm getting on Click of check box.
On basis of these value , i want to filter this given array allResponse .
Values can be multiple like:
Filter the array where name is ABC and section is A and D,
Like after getting value i tried to create an object to filter like given below to filter iteratively. But not able to do it
var filterLiterals= [{"name": "ABC"},{"section": "A"},{"section": "D"}];
or
var filterLiterals= ["ABC","A","D"];
So result should be
{
"name": "ABC",
"class": "8",
"section": "A",
},
I'm confused which approach should follow to get the result.
I tried it by using || and && operator but not able to get the result what i want.
Could anybody please suggest me to suitable and proper approach to filter this array. I'm not able to do it.
Is it possible to filter in this way in javascript in any way...?
You can create a function which will accept the name and an array of section. Inside the function use filter and in the call back function return those object whose name matches and the section is included in the array of section passed to the function
var allResponse = [{
"name": "ABC",
"class": "8",
"section": "A",
},
{
"name": "DEF",
"class": "7",
"section": "B",
},
{
"name": "ABC",
"class": "8",
"section": "D",
}
]
function filterArray(name, sectionArray) {
return allResponse.filter(function(elem) {
return elem.name = name && sectionArray.includes(elem.section)
})
};
console.log(filterArray('ABC', ['A', 'D']))
If I'm reading your question correctly you want to filter your data based on what you clicked.
var allResponse = [
{
"name": "ABC",
"class": "8",
"section": "A",
},
{
"name": "DEF",
"class": "7",
"section": "B",
},
{
"name": "ABC",
"class": "8",
"section": "D",
},
];
var clickedValue = "ABC";
var filteredResponse = allResponse.filter( function(response) {
return response.name === clickedValue
});
console.log(filteredResponse);
I think this should resolve your filtering problem.
In my understanding, I need to find elements satisfying below,
name: ABC
section: A or D
var allResponse = [
{
"name": "ABC",
"class": "8",
"section": "A",
},
{
"name": "DEF",
"class": "7",
"section": "B",
},
{
"name": "ABC",
"class": "8",
"section": "D",
},
];
var filterLiterals = [
{"name": "ABC"},
{"section": "A"},
{"section": "D"}
];
const parsed = filterLiterals.reduce((acc, obj) => {
const key = Object.keys(obj)[0];
if (typeof acc[key] === 'undefined')
acc[key] = [obj[key]];
else
acc[key].push(obj[key]);
return acc;
}, {});
//console.log("parsed: ", parsed);
var answer = allResponse.filter(obj => {
let flag = true;
for (const key of Object.keys(parsed)) {
if (!parsed[key].includes(obj[key])) {
flag = false;
break;
}
}
return flag;
});
console.log(answer);
Run this!

Build a JSON tree from materialized paths

I'm planning on using materialized paths in MongoDB to represent a tree and need to convert the materialized paths back into a JSON tree.
ex.
// Materialized path
var input = [
{"id": "0", "path": "javascript" },
{"id": "1", "path": "javascript/database" },
{"id": "2", "path": "javascript/database/tree" },
{"id": "3", "path": "javascript/mvc" },
{"id": "4", "path": "javascript/mvc/knockout.js"},
{"id": "5", "path": "javascript/mvc/backbone.js"},
{"id": "6", "path": "c++" },
{"id": "7", "path": "c++/c0xx"},
{"id": "8", "path": "c++/c0xx/lambda expressions"},
{"id": "9", "path": "c++/c0xx/vc10" }
];
The result would be:
[
{
"id": "0",
"name": "javascript",
"children": [
{
"id": "1",
"name": "database",
"children": [
{
"id": "2",
"name": "tree",
"children": []
}
]
},
{
"id": "3",
"name": "mvc",
"children": [
{
"id": "4",
"name": "knockout.js",
"children": []
},
{
"id": "5",
"name": "backbone.js",
"children": []
}
]
}
]
},
{
"id": "6",
"name": "c++",
"children": [
{
"id": "7",
"name": "c0xx",
"children": [
{
"id": "8",
"name": "lambda expressions",
"children": []
},
{
"id": "9",
"name": "vc10",
"children": []
}
]
}
]
}
]
I found Convert delimited string into hierarchical JSON with JQuery which works fine.
And I also found Build tree from materialized path which is written in Ruby and uses recursion. I'm interested and curious to see this implemented in Javascript and wonder whether there are any folks that are fluent in both Ruby and Javascript who would like to rewrite it. I did try a Ruby to JS converter, but the result was incomprehensible.
Thanks,
Neville
var Comment = new Schema({
date : {
type : Date,
default : Date.now
},
event: ObjectId,
body : String,
pathComment : String,
user: Array
})
Comment.virtual('level').get(function() {
return this.pathComment.split(',').length;
});
Comment.find({event: event.id}).sort({pathComment:1}).exec(function(err, comment){
var collectComment = function(comment){
return {
body: comment.body,
event: comment.event,
pathComment: comment.pathComment,
id: comment._id,
level: comment.level,
user: comment.user[0],
date: comment.date,
comments: []
};
}
var tplComment = [];
var createChildComment = function(comment, currentNode, level){
if(level==1){
comment.push(collectComment(currentNode));
}else{
createChildComment(comment[comment.length-1]['comments'], currentNode,level-1);
}
return;
}
for(var k in comment){
createChildComment(tplComment, comment[k],comment[k].level);
}
});

Categories

Resources