$filter by nested object values only - javascript

I am filtering JSON data against an array of strings. An example of $scope.Data is below:
{
"exerciseDescription": "Lean forward onto a chair and allow the arm to hang down, gently swing the arm from side to side",
"exerciseID": "324",
"exerciseName": "Shoulder Pendular Exercise (Adduction/Abduction)",
"images": [
1008,
1009,
1010
],
"tags": [
"Body Part",
"Arm",
"Upper body",
"Equipment",
"Chair"
"Soft Tissue"
]
},
There is a total of 4500 sets of this data and I would like to filter it by clicking on checkboxes. On clicking a checkbox I push the value of the checkbox (which will be a tag) to an array.
I would then like to filter against ONLY the the nested tag values.
My watch function is here:
$scope.$watchCollection('ActiveFilters', function(newValue) {
if ($scope.ActiveFilters.length > 0) {
$scope.FilteredData = $scope.Data;
for (var i = 0; i < $scope.ActiveFilters.length; i++) {
$scope.FilteredData = $filter('filter')($scope.FilteredData, $scope.ActiveFilters[i]);
console.log($scope.FilteredData);
// console.log($scope.ActiveFilters);
}
}
else {
$scope.FilteredData = [];
}
});
So if $scope.FilteredData contains any 'ActiveFilters' in its nestedtag` array then it will show in the scope.
In brief - how can I only filter against the nested tag array.

Filtering by an value of object in array in javascript:
var items = [{
"exerciseDescription": "Lean forward onto a chair and allow the arm to hang down, gently swing the arm from side to side",
"exerciseID": "324",
"exerciseName": "Shoulder Pendular Exercise (Adduction/Abduction)",
"images": [
1008,
1009,
1010
],
"tags": [
"Body Part",
"Arm",
"Upper body",
"Equipment",
"Chair",
"Soft Tissue"
]
}];
var filter = function (obj) {
if (obj['tags'].indexOf("Soft Tissue") != -1) { // <--- filter by value in tags array
return true;
}
return false;
}
var filtered = items.filter(filter);
I think you will get the idea and adapt it to use with angular.

I don't think there's a good reason to use $filter unless you're trying to filter in your AngularJS markup/templates. It's only really useful in JS if you want to support AngularJS filter expressions in custom directives.
Here's a more complete example for doing a tag filter in vanilla JavaScript:
var filterTags = ['foo', 'bar', 'baz'];
var incomingData = [ // simplified example
{id: 'banana', tags: ['foo', 'qux']},
{id: 'potato', tags: ['qux', 'baz', 'foo', 'bar']},
{id: 'carrot', tags: ['qux', 'quux']},
{id: 'cabbage', tags: ['foo', 'baz', 'bar']}
];
var dataMatchingAllTags = incomingData.filter(function (obj) {
return filterTags.every(function (tag) {
return obj.tags.indexOf(tag) !== -1;
});
}); // [{id: 'potato', …}, {id: 'cabbage', …}]
var dataMatchingAnyTag = incomingData.filter(function (obj) {
return filterTags.some(function (tag) {
return obj.tags.indexOf(tag) !== -1;
});
}); // [{id: 'banana', …}, {id: 'potato', …}, {id: 'cabbage', …}]
var dataMatchingTagsExactly = incomingData.filter(function (obj) {
return (
obj.tags.length === filterTags.length &&
filterTags.every(function (tag) {
return obj.tags.indexOf(tag) !== -1;
})
);
}); // [{id: 'cabbage'}]
In your case $scope.ActiveFilters would be filterTags, $scope.Data would be incomingData and $scope.FilteredData would be either of dataMatchingAllTags, dataMatchingAnyTag or dataMatchingTagsExactly, depending on how you want the filters to work.
Note that this example assumes ES5 but considering AngularJS does so as well, I don't think this will be a problem.

Related

How can I make this array mapping in a more elegant way in Javascript?

I am trying to get categories arrays from all objects of the main array and merge them in one array.
const allItems = res.map((item) => item.categories)[0]
How can it be done correctly?
Well Why you map all the items and get only the first one?
You can:
const res = [
{categories: "a"},
{categories: "b"},
{categories: "c"},
];
const categories = res[0].categories;
console.log(categories);
I think you are trying to put all elements of categories array into the same array (which at start is an array of objects).
So, since you didn't put any code, I suppose allItems is the following:
var allItems = [
{
one: "a",
categories: ["cat1", "cat2", "cat3"]
},
{
two: "b",
categories: ["cat4", "cat5", "cat6"]
},
{
three: "c",
categories: ["cat7", "cat8", "cat9"]
}
]
using
allItems = allItems.map(item =>item.categories).flat()
makes allItems filled of all the elements of item.categories
["cat1", "cat2", "cat3", "cat4", "cat5", "cat6", "cat7", "cat8", "cat9"]

Creating an object from API data and adding it to an array dynamically

I have a data set that I'm pulling in from a database. It's one dimensional and I basically need to make it more structured. I refer to it as "flat".
I need to display a heading, and items under that heading that are related to the heading.
The data comes in as having and section_name (the heading) and item_name (items) and other data unique to each item like download URLs etc.
item_name(item)_______section_name(header)
first_________________Funds
second________________Funds
third_________________Funds
fourth________________Literature
fifth_________________Literature
sixth_________________Literature
seventh_______________Literature
eighth________________DueDilligence
I don't know what any of the names will be for the items or sections, or how many items, sections, or items per section. As I said, it's very flat. This needs to be fully dynamic which is why this is complicating things for me.
Here is what I've done.
API call to retrieve data. Store data in a state as an array (it comes in as an array of objects).
I create an empty array to store my newly structured data.
I loop through the data with a foreach.
I create a new object for my new data to add to the new array so I can loop over it later.
I first check to make sure the data exists.
To create the headers I check to see if my new empty array is actually empty OR my section_name is not the same as the last one.(in the original data array I got from the API call)
I store the section_names as an object in the new array (newArray.push(newObject)
I've gotten this far. Now I need to take the item_names that correlates to the section_names and store them in the object under each header name, or at least in the same index.
_generateInfo() {
let dataArray = this.state.stepTwoData
let newArray =[]
dataArray.forEach(function(item, index) {
let newObject = {}
if (index > 0) {
if (newArray.length === 0 || item.investor_portal_section_name !== dataArray[index -1].investor_portal_section_name) {
newObject["name"] = item.investor_portal_section_name
newObject["items"] = []
newArray.push(newObject)
}
})
console.log(newArray)
}
I tried pushing the items to the "number" array on my new object and that doesn't seem to work properly. Sometimes it will duplicate my newObject.name
Checking if the newObject.name === the section_names in the array and push it to the "number" array in my new object just creates new key-value pairs so it's still not correlating.
I tried looping through again in the if statement and if section_name === newObject.name then create a newObject and push it, but it would only push one of the items repeatedly instead of going through all of them.
I need to loop through and create a header (one header per different section_name). Then add each item that corresponds to the section_name to it. like this
[
{section_name(header): "Funds",
items: [
{
name: item_name,
sku: item_sku,
url: item_url
},
{
name: item_name,
sku: item_sku,
url: item_url
}]
},
{section_name(header):"Literature",
items: [
{name: item_name,
sku: item_sku,
url: item_url
},
{
name: item_name,
sku: item_sku,
url: item_url
}]}
]
Using associative array (dictionary) to segregate you data itmes by categories will do the job.
I've drafted some POC code that illustrates the idea. The key element there is buildAssociativeArray function
const raw_data = [
{item_name: "first", section_name: "Funds"},
{item_name: "second", section_name: "Funds"},
{item_name: "third", section_name: "Funds"},
{item_name: "fourth", section_name: "Literature"},
{item_name: "fifth", section_name: "Literature"},
{item_name: "sixth", section_name: "Literature"},
{item_name: "seventh", section_name: "Literature"},
{item_name: "eighth", section_name: "DueDilligence"},
]
function buildAssociativeArray(data) {
const dictionary = {};
for (var i = 0; i < data.length; i++) {
const item = data[i];
const section = item.section_name;
var dictEntry = dictionary[section];
if (!dictEntry) {
dictEntry = [];
dictionary[section] = dictEntry;
}
dictEntry.push({
name: item.item_name,
// other fields like sku: item_sku or url: item_url may follow here
});
}
return dictionary;
}
const dictionary = buildAssociativeArray(raw_data);
console.log(dictionary);
/*
At this point
dictionary == {
"Funds": [
{
"name": "first"
},
{
"name": "second"
},
{
"name": "third"
}
],
"Literature": [
{
"name": "fourth"
},
{
"name": "fifth"
},
{
"name": "sixth"
},
{
"name": "seventh"
}
],
"DueDilligence": [
{
"name": "eighth"
}
]
}
*/
// Associcative array dictionary itself allows to further solve you task using for (var key in dictionary) {...} operator
// If however you need to obtain the data structure looking exactly like the one in your question you may go further with following function
function transformAssociativeArray(dictionary) {
const array = [];
for (var key in dictionary) {
const items = dictionary[key];
const newEntry = {
section_name: key,
items: items,
}
array.push(newEntry);
}
return array;
}
const array = transformAssociativeArray(dictionary);
console.log(array);
/*
At this point
array == [
{
"section_name": "Funds",
"items": [
{
"name": "first"
},
{
"name": "second"
},
{
"name": "third"
}
]
},
{
"section_name": "Literature",
"items": [
{
"name": "fourth"
},
{
"name": "fifth"
},
{
"name": "sixth"
},
{
"name": "seventh"
}
]
},
{
"section_name": "DueDilligence",
"items": [
{
"name": "eighth"
}
]
}
]
*/

Sorting array of object inside reduce in JavaScript

I am trying to do sort inside the reduce and I thought I have everything correct but still my result is not sorted as desired.
Here is the code snippet I have:
var studentInfo = [
{
studentId: 1,
addresses: [
{street: '123 Main St'},
]
},
{
studentId: 2,
addresses: [
{street: '456 Front St'}
]
},
{
studentId: 3,
addresses: [
{street: '100 MLK St'}
]
}
];
function appendAddress(studentId, newAddress) {
return studentInfo.reduce(function (info, student) {
if (student.studentId === studentId) {
student = {
studentId: student.studentId,
addresses: student.addresses.concat(newAddress).sort(function (address1, address2) {
return address2.street - address1.stree;
})
};
}
info.push(student);
return info;
}, []);
}
var newAddress = {
street: '166 Devil St'
}
console.log('Result: ' + JSON.stringify(appendAddress(2, newAddress)));
I am getting result as
Result: [{"studentId":1,"addresses":[{"street":"123 Main St"}]},{"studentId":2,"addresses":[{"street":"456 Front St"},{"street":"166 Devil St"}]},{"studentId":3,"addresses":[{"street":"100 MLK St"}]}]
instead of
Result: [{"studentId":1,"addresses":[{"street":"123 Main St"}]},{"studentId":2,"addresses":[{"street":"166 Devil St"},{"street":"456 Front St"}]},{"studentId":3,"addresses":[{"street":"100 MLK St"}]}]
Am I missing anything?
As to the sorting issue, if that was the main thing you were wondering about, you do indeed have a typo as the comment above noted, and also, performing subtraction on non-numeric strings won't get you very far. I used .localeCompare in the solution above.
If you wanted to copy the objects as you were appending, that can still be done more simply, but I don't know if that's what you actually want.
var studentInfo = [
{studentId: 1,addresses: [{street: '123 Main St'}]},
{studentId: 2,addresses: [{street: '456 Front St'}]},
{studentId: 3,addresses: [{street: '100 MLK St'}]}
];
console.log(addAddress(2, {street: "1234 56th Ave"}));
function addAddress(studentId, address) {
const idx = studentInfo.findIndex(o => o.studentId === studentId);
if (idx !== -1) {
return [...studentInfo.slice(0, idx), {
studentId,
addresses: [...studentInfo[idx].addresses, address].sort((a,b) => a.street.localeCompare(b.street))
}, ...studentInfo.slice(idx+1)];
} else {
return [...studentInfo, {studentId, addresses:[address]}];
}
}
But now you're having two different copies of the data with some shared objects.

Iterating a javascript collection and adding a sort order

I have a tree in javascript which has multiple root elements and nested children.
Here's the object:
[{
_id: '546d30905d7edd1d5169181d',
name: 'first'
children: []
}, {
_id: '546d30905d7edd1d2169181d',
name: 'second'
children: []
}, {
_id: '446d30905d7edd1d5169181d',
name: 'third',
children: [{
_id: '446d30905d7e2d1d5169181d',
name: '3child',
children: []
}, {
_id: '446d30915d7e2d1d5169181d',
name: '3child2',
children: [{
_id: '546d30905d7edd1d2569181d',
name: 'second2',
children: []
}]
}]
}, {
_id: '546d30995d7edd1d5169181d',
name: 'fourth',
children: []
}]
This is a truncated document that's being stored in MongoDB using materialized path. The issue is that I need to add a 'sorting' ability, so nodes in the same root can be sorted.
I want to iterate this tree and apply a sort_value such as node['sort_value'] = 0, etc.
Each level will have it's own sort order, starting at 0.
I can simply iterate the tree recursively:
function iterate(items) {
_.each(items, function(page, key) {
if (items.children.length > 0) {
iterate(items.children);
}
});
}
However, I can't figure out how to keep track of the sort orders and also update the object's to include the sort_value field.
Any help would be greatly appreciated! Thank you
I did it so that I used array key for sorting and "synchronized" it with object property (because I needed it saved to DB and restored after) and it works as a charm :)
So something like this, pseudo:
var unsorted = [
0:{"sort_key": "0", "data":"dataaa 0"},
1:{"sort_key": "1", "data":"dataaa 1"},
...
n:{"sort_key": "n", "data":"dataaa n"}
];
function_sort(unsorted){
...
return sorted = [
0:{"sort_key": "n", "data":"dataaa y"},
1:{"sort_key": "44", "data":"dataaa x"},
...
n:{"sort_key": "0", "data":"dataaa z"}
];
}
save = function_save(sorted){
...update sort_key as array key...
return for_saving;
}

How to optimize array lookup of JS object based on one property

We have a large array of objects:
var englishStudents = [
{StudentId: 1, Name: "John"},
{StudentId: 2, Name: "Jack"},
{StudentId: 3, Name: "Jane"}
];
Need to check if another similar object is contained in this array, just by comparing one property alone.
var randomStudent = {StudentId: 1337, Name: "Foo"};
This is what I have, and it seems as though it will work, but I don't think this is the best way to do this.
var studentIds = $.map(englishStudents, function (student, index) { return student.StudentId; });
var randomStudentLearnsEnglish = false;
for (var sId in studentIds) {
if (randomStudent.StudentId == sId) {
randomStudentLearnsEnglish = true;
break;
}
}
What would be the optimized way to do this?
You should keep student data in a hash table like JHashtable instead of the array. For mor complex scenarios, you can maintain more than one hash table, like studentsById, studentsByCountryCode, etc.
If all you want to know is if the ID exists can do this:
function checkIdExists( id){
/* map array of matching ID, if none exists length of array is zero*/
return $.map(englishStudents, function (student, index) {
return student.StudentId==id;
}).get().length;
});
Use:
if( checkIdExists( 1234)){
/* run exists code*/
}
If you really want, you can create a further indexing scheme:
var englishStudents = [
{StudentId: 1, Name: "John"},
{StudentId: 2, Name: "Jack"},
{StudentId: 3, Name: "Jane"}
];
//if doing this a lot of time, take the one time hit of construction and memory
var idToNameMap = createIdToNameMap(englishStudents); //returns {'1': 'John', '2': Jack' , '3': 'Jane'}
var randomStudent = getRandomStudent();
if( idToNameMap[ randomStudent.StudentId] != undefined){ ... }
Just do a hash instead of an array, so:
var englishStudents = {
1: {StudentId: 1, Name: "John"},
2: {StudentId: 2, Name: "Jack"},
3: {StudentId: 3, Name: "Jane"}
};
and then to retrieve, simply do:
var student = englishStudents[id];

Categories