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 logic like below,
getSpecificCell: function(tableObject, rowText, columnCss) {
var ele = element.all(by.repeater(tableObject)).count().then(function(count) {
for (var i = 0; i < count; i++) {
return element(by.repeater(tableObject).row(i)).getText().then(function(txt) {
if (txt.indexOf(rowText) !== -1) {
return element(by.repeater(tableObject).row(i)).element(by.css('[' + columnCss + ']'));
}
});
}
});
return ele;
}
But it is returning the value in first iteration itself.
Is that possible to return the promise inside this kind of for loop or do we have any other solution for this?
First, you don't need to use for loops with an ElementArrayFinder. That's what the each() method is for.
Second, you shouldn't need to loop at all. It sounds like you should be using filter() to get the table cells that match your specification, though I'm not sure what exactly you're trying to accomplish.
var table = element.all(by.repeater(tableObject));
// list is an ElementArrayFinder of all elements that matched the filter
var list = table.filter(function (elem) {
return elem.getText().then(function (text) {
return txt.indexOf(rowText) !== -1
})
});
// do something with list
list.count().then(function (count) {
console.log(count);
});
I am very new to angular and, I am not sure how to control the behavior of my filters.
In the app, I have two different single-select drop down controls that filter the results of my data set and fill a table. However, even though these filters work, the results are dependent of both controls and if both are not being used , the empty set is returned. So, my question is: How can I use these filters optionally? So, the app returns every result when the filters are not used or returns the filtered results by one of the controls or both?
Thank you
Here is the code:
AngularJS
The filters for each control. They look very similar:
.filter('byField', function () {
return function (results, options) {
var items = { options: options, out: [] };
angular.forEach(results, function (value, key) {
for (var i in this.options) {
if ((options[i].value === value.fieldId &&
options[i].name === "Field" &&
options[i].ticked === true)) {
this.out.push(value);
}
}
}, items);
return items.out;
};
})
.filter('byClass', function () {
return function (results, options) {
var items = { options: options, out: [] };
angular.forEach(results, function (value, key) {
for (var i in this.options) {
if ((options[i].value === value.documentClass &&
options[i].name === "Class" &&
options[i].ticked === true)) {
this.out.push(value);
}
}
}, items);
return items.out;
};
})
HTML
This is what I am doing to populate the rows of the table:
<tr ng-repeat="result in results | byField:outputFields | byClass:outputClasses">
<td>{{result.documentId}}</td>
...
</tr>
Dorado7.1 in all event listeners provides a view implicit variable pointing to the current event host's view, the variable can completely replace the use of this scenario.
Well, as I imagined the answer was more related to set theory than to angular.
I just made an union between the empty set and every result, and it worked.
.filter('byField', function () {
return function (results, options) {
var items = { options: options, out: [] };
angular.forEach(results, function (value, key) {
if (options.length) {
for (var i in this.options) {
if ((options[i].value === value.fieldId &&
options[i].name === "Field" &&
options[i].ticked === true)) {
this.out.push(value);
}
}
} else {
this.out = results.slice();
}
}, items);
return items.out;
};
})
I have items which should have multiple (e.g. categories). Now I want to filter my items to these categories.
I think the task is not possible with the filter-directive without using a custom filter, right?
I came up with a solution, but it looks dirty and wrong to me:
$scope.filterList = function (item) {
var found = false;
var allFalse = true;
angular.forEach(item.attributes, function (value, key) {
if ($scope.activeAttributes[value.name] === true) {
found = true;
}
});
angular.forEach($scope.activeAttributes, function (value, key) {
if (value === true) {
allFalse = false;
}
});
$log.log("length: " + Object.keys($scope.activeAttributes).length);
if (found === true || Object.keys($scope.activeAttributes).length === 0 || allFalse === true) {
return true;
}
};
Demo JSFiddle of my code
I thought with Angular, that the code should be simple and most of the work should be done by Angular. What if I need to filter more attributes?
I have create a filter but this filter is not working with array inside array.
'http://plnkr.co/edit/oygy79j3xyoGJmiPHm4g?p=info'
Above plkr link is working demo.
app.filter('checkboxFilter', function($parse) {
var cache = { //create an cache in the closure
result: [],
checkboxData: {}
};
function prepareGroups(checkboxData) {
var groupedSelections = {};
Object.keys(checkboxData).forEach(function(prop) {
//console.log(prop);
if (!checkboxData[prop]) {
return;
} //no need to create a function
var ar = prop.split('=');
//console.log("ar is - "+ar);
if (ar[1] === 'true') {
ar[1] = true;
} //catch booleans
if (ar[1] === 'false') {
ar[1] = false;
} //catch booleans
/* replacing 0 with true for show all offers */
if(ar[0]=='SplOfferAvailable.text'){
ar[1]='true';
}else{
}
//make sure the selection is there!
groupedSelections[ar[0]] = groupedSelections[ar[0]] || [];
//at the value to the group.
groupedSelections[ar[0]].push(ar[1]);
});
return groupedSelections;
}
function prepareChecks(checkboxData) {
var groupedSelections = prepareGroups(checkboxData);
var checks = [];
//console.log(groupedSelections);
Object.keys(groupedSelections).forEach(function(group) {
//console.log("groupedSelections- "+groupedSelections);
//console.log("group- "+group);
var needToInclude = function(item) {
//console.log("item- "+item);
// use the angular parser to get the data for the comparson out.
var itemValue = $parse(group)(item);
var valueArr = groupedSelections[group];
//console.log("valueArr- "+valueArr);
function checkValue(value) { //helper function
return value == itemValue;
}
//check if one of the values is included.
return valueArr.some(checkValue);
};
checks.push(needToInclude); //store the function for later use
});
return checks;
}
return function(input, checkboxData, purgeCache) {
if (!purgeCache) { //can I return a previous 'run'?
// is the request the same as before, and is there an result already?
if (angular.equals(checkboxData, cache.checkboxData) && cache.result.length) {
return cache.result; //Done!
}
}
cache.checkboxData = angular.copy(checkboxData);
var result = []; // this holds the results
//prepare the checking functions just once.
var checks = prepareChecks(checkboxData);
input.every(function(item) {
if (checks.every(function(check) {
return check(item);
})) {
result.push(item);
}
return result.length < 10000000; //max out at 100 results!
});
cache.result = result; //store in chache
return result;
};
});
above code is for check box filter.
when i click on checkbox called "Availability" it does not filter the result.
Please help me out.
Thanks.
I think that the way you are navigating through json is wrong because if you put in this way it works
"Location": "Riyadh",
"AvlStatus": "AVAILABLE"
"Rooms": {.....
You have to go in some way through Rooms and right now I think you're not doing that