I'm using Windows Azure Mobile Service to build the backend for my app. For server script's read operation, now I want to retrieve the query parameter like $filter, $select in the script, etc. Any idea?
After hacking around with the 'query' object in the 'read' function's parameter (by using console.log ), I finally found the solution:
function isObject(variable) {
return variable !== null &&
variable !== undefined &&
typeof variable === 'object';
}
// Find all the member-value pairs from the expression object
function findMemberValuePairsFromExpression (expr, ret) {
if (!isObject(expr)) {
return null;
}
ret = ret || {};
for (var name in expr) {
if (expr.hasOwnProperty(name)) {
var prop = expr[name];
if (name === 'parent') { // Ignore parent property since it's added by us
continue;
}
else if (name === 'left') { // member expression are in the left subtree
if (isObject(prop)) {
prop.parent = expr; // Remember the parent
findMemberValuePairsFromExpression(prop, ret);
}
}
else if (name === 'member') {
// Found a member expression, find the value expression
// by the knowledge of the structure of the expression
var value = expr.parent.right.value;
ret[prop] = value;
}
}
}
if (expr.parent) {
// Remove the added parent property
delete expr.parent;
}
return ret;
}
// Get the filters component from query object and
// find the member-value pairs in it
function findMemberValuePairsFromQuery (query) {
var filters = query.getComponents().filters;
return findMemberValuePairsFromExpression(filters);
}
function read (query, user, request) {
request.execute();
}
Remember that this approach heavily relies on the inner structure of the query object so it may break in the future.
query.getComponents() also returns other parts of the query, like 'select', 'skip', 'top', etc. Basically anything of the oData protocol
Related
I have two tabs in my admin interface. I am storing the response in my session storage. When I do the updation of any records in the tab or if I insert new record also, the same thing should be reflected in the storage also. But currently, the changes are not getting reflected in the storage. I tried my best to sort out, but I could not able to succeed. Any help/advice greatly appreciated.
Angularjs:
$scope.Pool = [];
if (!localStorageService.get('Pool')) {
Role.getPool().success(function(data) {
if (data.responseCode === 0) {
_.forEach(data.response.demoPool, function(value, key) {
dataObj = {};
dataObj.id = value.poolId;
dataObj.value = value.poolName;
$scope.Pool.push(dataObj);
});
localStorageService.set('Pool', $scope.Pool);
} else {
$scope.alerts.alert = true;
$scope.alerts.type = 'danger';
$scope.alerts.msg = data.errorMsg;
}
})
First time it will do because !localStorageService.get('Pool') becomes true. But next time it will return false because storage has value already, it will not get inside the if condition. so to resolve this remove the session storage 'Pool' to allow to execute your Role.getPool().success(function(data) {
if (!sessionStorage.length) {
// Ask other tabs for session storage
localStorage.setItem('getSessionStorage', Date.now());
};
window.addEventListener('storage', function (event) {
switch (event.key) {
case 'getSessionStorage':
// Some tab asked for the sessionStorage -> send it
localStorage.setItem('sessionStorage', JSON.stringify(sessionStorage));
localStorage.removeItem('sessionStorage');
break;
case 'sessionStorage':
// sessionStorage is empty -> fill it
var data = JSON.parse(event.newValue);
for (key in data) {
sessionStorage.setItem(key, data[key]);
}
break;
}
});
I am new to react and not able to find how can i get the code coverage done for my below mentioned function using jest.
For the sake of clarity I have created a function just similar to what I am using :
setName: function setName () {
var storage = localStorage.getItem(user);
var session = sessionStorage.getItem(temp);
if ((session === null || session === undefined) && (storage === null && storage === undefined)) {
var name = "window.name";
if (name === 'John') {
changeName(name);
} else if (name === 'Jane') {
changeName(name);
} else if (name === 'Mark') {
changeName(name);
} else {
changeName('John');
}
} else if (session !== null) {
changeName(session);
} else if (storage !== null) {
changeName(storage);
}
}
Any help is appreciated
You need to be able to specify the name 'John', 'Jane', etc. To do this with your sample you will need to save the appropriate data into localStorage and sessionStorage before running the test.
In your test file:
localStorage.setItem('John');
sessionStorage.setItem('A Session');
// do the test now and test what you expect to get back when the user is John.
localStorage.setItem('Jane');
// do the test now and test what you expect to get back when the user is Jane.
The tests could be done with a loop and arrays if the results being tested for are basic and you can simply do a foreach or similar.
testData = [
{ Name: 'John', Result: true},
{ Name: 'Jane', Result: false}
];
testData.forEach(function(testItem) {
expect(setName(testItem.Name)).toBe(testItem.Result);
}
Another option is to move the access to local and session storage out of your function setName() and then use dependency injection or other functions in your code to get these values. Then you can set up a mock (or dummy function) for returning the data for the storage which returns hard set literal data to test each part of the if() structure.
I am building an application in NodeJS and AngularJS.
I am building a multi-column search functionality where the user can type in search keywords into separate searchboxes (at the top of each column) and retrieve the results based on the column.
So far I have a single searchbox that searches all attributes at the same time.
How can I implement multiple individual searchboxes that will return results based on multiple attributes?
Note: I want to implement this on the server-side for performance reasons. (I know that I can simply use HTML attributes | filter:column1 | filter:column2 but want to avoid this technique if possible).
Here is the code I have so far. I am thinking that I need to pass in some sort of "searchBy" variable that is set on the view and then update the search method to search by multiple query/attribute pairs.
//Search service factory
//Initialize filtered items and get search results
function search(items, query) {
this.filteredItems = $filter('filter')(items, function (item) {
for(var attr in item) {
if (searchMatch(item[attr], query))
return true;
}
return false;
});
return this.filteredItems;
}
function searchMatch(haystack, needle) {
if (!needle) {
return true;
}
return haystack.toString().toLowerCase().indexOf(needle.toLowerCase()) !== -1;
};
//Controller
vm.filteredItems = vm.search(vm.unfilteredItems, vm.query);
//View
input(type='text', ng-model='vm.query', ng-change='vm.search(vm.unfilteredItems, vm.query)', placeholder='Search')
I was able to solve this by first creating an array of objects for each search box then repeating those boxes in the view with the ng-repeat attribute.
//Controller
var vm = this;
var vm.unfilteredItems; //data source query removed for brevity
//Initialize search inputs
vm.search_by_inputs = [
{search_column: 'id', search_query: ''},
{search_column: 'requester', search_query: ''},
{search_column: 'dataowner', search_query: ''}
];
function initSearch() {
vm.filtered_items = vm.search(vm.unfiltered_items, vm.search_by_inputs);
}
//View
input.input-large.search-query(type='text', value='{{search_by.search_query}}', ng-model='search_by.search_query' ng-change='vm.initSearch()', placeholder='Search')
The next step is to loop over the search_by_inputs object in the controller and create a new object with only the inputs that have search values entered into the searchboxes in the view. Then in the search method the built-in "filter" component iterates each item, and inside that loop each of the search terms is checked against that value with the column name that matches the property.
/*
* Create new array of objects with only elements that have search values to optimize loop inside filter
* #search_by_inputs array of objects each has a key search_column and a value search_query
*/
function optimizeSearchProperties(search_by_inputs) {
search_by_properties = [];
for (var i = 0, len = search_by_inputs.length; i < len; i++) {
//If this column input box has query text
if (search_by_inputs[i].search_query) {
search_by_properties.push(search_by_inputs[i]);
}
}
return search_by_properties;
}
/*
* #haystack search item
* #needle search term
*/
function searchMatch(haystack, needle) {
if (!needle) {
return true;
}
return haystack.toString().toLowerCase().indexOf(needle.toLowerCase()) !== -1;
}
/*
* Create filtered items object by filtering search results
* #items original array of objects returned by database query result
* #search_by_inputs array of objects each has a key search_column and a value search_query
*/
function search(items, search_by_inputs) {
var search_by_properties = optimizeSearchProperties(search_by_inputs);
//If there are no search properties input by requester then return all items
if (search_by_properties.length === 0) {
this.filtered_items = items;
return this.filtered_items;
}
this.filtered_items = $filter('filter')(items, function (item) {
var search_result = true;
//Loop over all search by input textboxes
for (var n = 0, len = search_by_properties.length; n < len; n++) {
//If there is no query text
if (!search_by_properties[n].search_query) {
//Continue to next element in array
continue;
//Else if element has a property that matches search input column name
} else if (item[search_by_properties[n].search_column]) {
if (!searchMatch(item[search_by_properties[n].search_column], search_by_properties[n].search_query)) {
search_result = false;
break;
}
}
}
return search_result;
});
return this.filtered_items;
}
I would be glad to have some feedback on this solution in terms of optimization, performance, technique, etc. Thanks!
I'm trying to filter a JSON array using another JSON array criteria that I have using (filter).
Here is my code:
function filterArray(object, criteria){
return object.filter(function(obj){
for(var i=0;i<criteria.length;i++){
let criteriaEle = criteria[i];
return Object.keys(criteriaEle).forEach(function(key){
if(obj[key] == criteriaEle[key]){
return obj;
}
})
}
})
}
For example:
object = [{type:1,company:1,color:0,name:a},{type:2,company:1,color:0,name:b},{type:1,company:3,color:0,name:c},{type:4,company:1,color:0,name:d},{type:1,company:1,color:1,name:e}]
criteria = [{type:1,company:1,color:0},{type:1,company:1,color:1}]
So if I give these two arrays to the function it should return
obj = [{{type:1,company:1,color:0,name:a},{type:1,company:1,color:1,name:e}}]
I'm not sure where am I going wrong in this. Please help.
Update:
Also, I do not want to use obj.type or obj.company or object.color as parameters to search as I want to make my code maintainable and do not want to come and update it later if in future more criteria's are added.
const data = [{type:1,company:1,color:0,name:'a'},{type:2,company:1,color:0,name:'b'},{type:1,company:3,color:0,name:'c'},{type:4,company:1,color:0,name:'d'},{type:1,company:1,color:1,name:'e'}];
const criteria = [{type:1,company:1,color:0},{type:1,company:1,color:1}];
function checkCriteria(obj) {
return criteria.some(criterion => {
for (const key in criterion) {
if (criterion[key] !== obj[key]) {
return false;
}
}
return true;
});
}
const filtered = data.filter(checkCriteria);
console.log('Filtered array: ', filtered);
Here is one solution.
Here are some references
Array.some
Array.filter
Based on the comment, adding another snippet to explain the concept of closures.
const data = [{type:1,company:1,color:0,name:'a'},{type:2,company:1,color:0,name:'b'},{type:1,company:3,color:0,name:'c'},{type:4,company:1,color:0,name:'d'},{type:1,company:1,color:1,name:'e'}];
function createCriteriaValidationFunction(criteria) {
return function checkCriteria(obj) {
return criteria.some(criterion => {
for (const key in criterion) {
if (criterion[key] !== obj[key]) {
return false;
}
}
return true;
});
}
}
const criteria = [{type:1,company:1,color:0},{type:1,company:1,color:1}];
const filtered = data.filter(createCriteriaValidationFunction(criteria));
console.log('Filtered array: ', filtered);
It's the same concept as before, however, criteria was defined in the file. This time, criteria can be defined outside and can be passed in to the function. The trick is to create the checkCriteria function on the fly with criteria passed in and available in the closure. In both cases, criteria variable is available in the scope in which checkCriteria is executed.
I have a requirement to add new objects to an existing array of objects where duplicates are not allowed and the uniqueness of the objects is based on all the properties of the object. The object contains properties:
1. departmentId
2. equipmentId
3. sectionId
4. serviceId
According to my requirements, equipmentId is an optional field i.e. it may be undefined when there is no value for it. I wrote below for it:
$scope.isDuplicateRecord = function(){
var dupCheck = _.findWhere($scope.equipmentServiceMaps,{departmentId:$scope.equipmentServiceMap.department.departmentId,
equipmentId:$scope.equipmentServiceMap.equipment.equipmentId,
sectionId:$scope.equipmentServiceMap.section.sectionId,
serviceId:$scope.equipmentServiceMap.service.serviceId})
if(dupCheck== undefined){
return false
}else{
return true
}
}
This code is working fine with all values of quipmentId except undefined, i.e. if there is an existing object with the same departmentId, sectionId, serviceId and equipmentId as undefined. It's not finding that object and returning undefined hence My check is failing.
When I change to code to the basic for loop it worked, but I want to go ahead with the underscore. Below is my workaround code with for loop,
var dupCheck = undefined;
for(var i = 0; i < $scope.equipmentServiceMaps.length; i++){
if($scope.equipmentServiceMaps[i].departmentId == $scope.equipmentServiceMap.department.departmentId
&& $scope.equipmentServiceMaps[i].sectionId == $scope.equipmentServiceMap.section.sectionId
&& $scope.equipmentServiceMaps[i].serviceId == $scope.equipmentServiceMap.service.serviceId
&& $scope.equipmentServiceMaps[i].equipmentId == ($scope.equipmentServiceMap.equipment? $scope.equipmentServiceMap.equipment.equipmentId : undefined)){
dupCheck = $scope.equipmentServiceMaps[i];
console.log(dupCheck);
break;
}
}
if(dupCheck == undefined){
return false
}else{
return true
}
Please suggest how can I go ahead with the underscore.js
If you were looking for some syntactic sugar, this may help:
var search = {
departmentId:$scope.equipmentServiceMap.department.departmentId,
equipmentId:$scope.equipmentServiceMap.equipment.equipmentId,
sectionId:$scope.equipmentServiceMap.section.sectionId,
serviceId:$scope.equipmentServiceMap.service.serviceId
}
_.find($scope.equipmentServiceMaps, function(obj) {
return _.isMatch(_.defaults({}, obj, attrs), attrs);
})
The normal _.findWhere function works more or less like this:
_.find($scope.equipmentServiceMaps, function(obj) {
return _.isMatch(obj, attrs);
})
In my version, any undefined values in the object will be substituted by values from the search attrs. The original object is unmodified.