Angularjs filtering json results - javascript

I'm having trouble filtering a json object using a filter function from a repeater. The filter was working on the first level without issue but now when I try to filter the rest of the collection and then return the scope data if the filter is not defined I get errors.
I've created a fiddle, I appreciate the help.
app.filter('showByfilValue',['ctrl'], function ($scope,ctrl) {
return function (items, word) {
var filtered = [];
if (typeof word !== 'undefined') {
var letterMatch = new RegExp('^' + word + '$', 'i');
for (var i = 0; i < items.length; i++) {
var item = items[i];
if (letterMatch.test(item.services[0].services[0].serviceName)) {
filtered.push(item);
}
}
return filtered;
} else {
return data;
}
};
});
Jimi

Related

AngularJs - check if value exists in array object

var SelectedOptionId = 957;
$scope.array = [{"957":"1269"},{"958":"1265"},{"956":"1259"},{"957":"1269"},{"947":"1267"}]
Is there a way of checking if a value exists in an that kind of array objects. I am using Angular and underscore.
I have tried all this -
if ($scope.array.indexOf(SelectedOptionId) === -1) {console.log('already exists')}
and
console.log($scope.array.hasOwnProperty(SelectedOptionId)); //returns false
and
console.log(_.has($scope.array, SelectedOptionId)); //returns false
You could use Array#some and check with in operator.
exists = $scope.array.some(function (o) {
return SelectedOptionId in o;
});
Check this
function checkExists (type) {
return $scope.array.some(function (obj) {
return obj === type;
}
}
var chkval=checkExists("your value")
Try this:
if($scope.array[SelectedOptionId] || _.includes(_.values($scope.array, SelectedOptionId))) { }
That should cover both a key and a value.
let selectedOptionId = "957";
let array = [{"957":"1269"},{"958":"1265"},{"956":"1259"},{"957":"1269"},{"947":"1267"}];
let filtered = array.filter(function(element){
return Object.keys(element)[0] === selectedOptionId;
});
console.log(filtered);
console.log(_.some($scope.array, function(o) { return _.has(o, "957"); }));
using underscore
You can use filter for this. The following code should return you output array with matching results, if it exists, otherwise it will return an empty array :
var array = [{"957":"1269"},{"958":"1265"},{"956":"1259"},{"957":"1269"},{"947":"1267"}];
var SelectedOptionId = 957;
var result = array.filter(
function(item) {return item[SelectedOptionId]}
)
console.log(result);
For your input it returns:
[ { '957': '1269' }, { '957': '1269' } ]
You can do it using the in operator or the hasOwnProperty function, to check for the existence of a key in an object inside the given array.
The way you've tried using hasOwnProperty function didn't work because you were checking it directly on the array instead of checking against the items in the array.
Check the below code snippet.
angular
.module('demo', [])
.controller('HomeController', DefaultController);
function DefaultController() {
var vm = this;
vm.items = [{
"957": "1269"
}, {
"958": "1265"
}, {
"956": "1259"
}, {
"957": "1269"
}, {
"947": "1267"
}];
var key = '957';
var isExists = keyExists(key, vm.items);
console.log('is ' + key + ' exists: ' + isExists);
function keyExists(key, items) {
for (var i = 0; i < items.length; i++) {
// if (key in items[i]) {
if (items[i].hasOwnProperty(key)) {
return true;
}
}
return false;
}
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="demo">
<div ng-controller="HomeController as home">
{{home.items | json}}
</div>
</div>
Different ways to do this :
Using Object hasOwnProperty() method.
Working demo :
var SelectedOptionId = 957;
var arrayObj = [{"957":"1269"},{"958":"1265"},{"956":"1259"},{"957":"1269"},{"947":"1267"}];
function checkOption(key) {
for(var i in arrayObj) {
if(arrayObj[i].hasOwnProperty(key) == true) {
return key+" exists.";
} else {
return key+" Not exists.";
}
}
};
console.log(checkOption(SelectedOptionId)); // 957 exists.
using Array filter() method.
Working demo :
var SelectedOptionId = 957;
var arrayObj = [{"957":"1269"},{"958":"1265"},{"956":"1259"},{"957":"1269"},{"947":"1267"}];
var result = arrayObj.filter(function(elem) {
return elem[SelectedOptionId]
});
if(result == '') {
console.log(SelectedOptionId+" not exists.");
} else {
console.log(SelectedOptionId+" exists.");
}
using Array some() method as suggested by Nina Scholz.
Working demo :
var SelectedOptionId = 957;
var arrayObj = [{"957":"1269"},{"958":"1265"},{"956":"1259"},{"957":"1269"},{"947":"1267"}];
var result = arrayObj.some(function (o) {
return SelectedOptionId in o;
});
if(result == '') {
console.log(SelectedOptionId+" not exists.");
} else {
console.log(SelectedOptionId+" exists.");
}

es6 code broken in es5

I have been trying to translate my code from es6 to es5 because of some framework restrictions at my work... Although I have been quite struggling to locate what the problem is. For some reason the code does not work quite the same, and there is no errors either ...
Can someone tell me If I have translated properly ?
This is the ES6 code :
function filterFunction(items, filters, stringFields = ['Title', 'Description'], angular = false) {
// Filter by the keys of the filters parameter
const filterKeys = Object.keys(filters);
// Set up a mutable filtered object with items
let filtered;
// Angular doesn't like deep clones... *sigh*
if (angular) {
filtered = items;
} else {
filtered = _.cloneDeep(items);
}
// For each key in the supplied filters
for (let key of filterKeys) {
if (key !== 'TextInput') {
filtered = filtered.filter(item => {
// Make sure we have something to filter by...
if (filters[key].length !== 0) {
return _.intersection(filters[key], item[key]).length >= 1;
}
return true;
});
}
// If we're at TextInput, handle things differently
else if (key === 'TextInput') {
filtered = filtered.filter(item => {
let searchString = "";
// For each field specified in the strings array, build a string to search through
for (let field of stringFields) {
// Handle arrays differently
if (!Array.isArray(item[field])) {
searchString += `${item[field]} `.toLowerCase();
} else {
searchString += item[field].join(' ').toLowerCase();
}
}
// Return the item if the string matches our input
return searchString.indexOf(filters[key].toLowerCase()) !== -1;
});
}
}
return filtered;
}
And this is the code I translated that partially 99% work ..
function filterFunction(items, filters, stringFields, angular) {
// Filter by the keys of the filters parameter
var filterKeys = Object.keys(filters);
// Set up a mutable filtered object with items
var filtered;
// Angular doesn't like deep clones... *sigh*
if (angular) {
filtered = items;
} else {
filtered = _.cloneDeep(items);
}
// For each key in the supplied filters
for (var key = 0 ; key < filterKeys.length ; key ++) {
if (filterKeys[key] !== 'TextInput') {
filtered = filtered.filter( function(item) {
// Make sure we have something to filter by...
if (filters[filterKeys[key]].length !== 0) {
return _.intersection(filters[filterKeys[key]], item[filterKeys[key]]).length >= 1;
}
return true;
});
}
// If we're at TextInput, handle things differently
else if (filterKeys[key] === 'TextInput') {
filtered = filtered.filter(function(item) {
var searchString = "";
// For each field specified in the strings array, build a string to search through
for (var field = 0; field < stringFields.length; field ++) {
// Handle arrays differently
console.log(field);
if (!Array.isArray(item[stringFields[field]])) {
searchString += item[stringFields[field]] + ' '.toLowerCase();
} else {
searchString += item[stringFields[field]].join(' ').toLowerCase();
}
}
// Return the item if the string matches our input
return searchString.indexOf(filters[filterKeys[key]].toLowerCase()) !== -1;
});
}
}
return filtered;
}
These two lines
searchString += `${item[field]} `.toLowerCase();
searchString += item[stringFields[field]] + ' '.toLowerCase();
are not equivalent indeed. To apply the toLowerCase method on all parts of the string, you'll need to wrap the ES5 concatenation in parenthesis:
searchString += (item[stringFields[field]] + ' ').toLowerCase();
or, as blanks cannot be lowercased anyway, just use
searchString += item[stringFields[field]].toLowerCase() + ' ';
Here is a translated code from babeljs itself, as commented above.
'use strict';
function filterFunction(items, filters) {
var stringFields = arguments.length <= 2 || arguments[2] === undefined ? ['Title', 'Description'] : arguments[2];
var angular = arguments.length <= 3 || arguments[3] === undefined ? false : arguments[3];
// Filter by the keys of the filters parameter
var filterKeys = Object.keys(filters);
// Set up a mutable filtered object with items
var filtered = void 0;
// Angular doesn't like deep clones... *sigh*
if (angular) {
filtered = items;
} else {
filtered = _.cloneDeep(items);
}
// For each key in the supplied filters
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
var _loop = function _loop() {
var key = _step.value;
if (key !== 'TextInput') {
filtered = filtered.filter(function (item) {
// Make sure we have something to filter by...
if (filters[key].length !== 0) {
return _.intersection(filters[key], item[key]).length >= 1;
}
return true;
});
}
// If we're at TextInput, handle things differently
else if (key === 'TextInput') {
filtered = filtered.filter(function (item) {
var searchString = "";
// For each field specified in the strings array, build a string to search through
var _iteratorNormalCompletion2 = true;
var _didIteratorError2 = false;
var _iteratorError2 = undefined;
try {
for (var _iterator2 = stringFields[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
var field = _step2.value;
// Handle arrays differently
if (!Array.isArray(item[field])) {
searchString += (item[field] + ' ').toLowerCase();
} else {
searchString += item[field].join(' ').toLowerCase();
}
}
// Return the item if the string matches our input
} catch (err) {
_didIteratorError2 = true;
_iteratorError2 = err;
} finally {
try {
if (!_iteratorNormalCompletion2 && _iterator2.return) {
_iterator2.return();
}
} finally {
if (_didIteratorError2) {
throw _iteratorError2;
}
}
}
return searchString.indexOf(filters[key].toLowerCase()) !== -1;
});
}
};
for (var _iterator = filterKeys[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
_loop();
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
return filtered;
}
p.s. Or there is a better way to use babeljs directly without manually converting it.

Angularjs splice in loop

I am building this angularjs app, its my first one. So huge learning curve. I cant seem to to delete items as well as my search doesn't work. I managed to get column sorting and pagination to work though.
I get no errors so i am not sure why the search and deleting of item row in table isnt working.
I have tried using this:
$scope.remove = function(client){
for(var i = $scope.clients.length - 1; i >= 0; i--){
if($scope.clients[i].name == client.name){
$scope.clients.splice(i,1);
}
}
}
Working example: http://plnkr.co/edit/f2ozbP4JxrRSvRGJAmQi
ANSWER, WORKING:
Thanks #DTing
$scope.remove = function(client){
$scope.filteredItems = $scope.filteredItems.filter(function(item) {
return item.name !== client.name;
});
$scope.groupToPages();
}
This should be your remove function:
$scope.remove = function(client){
for(var i = $scope.pagedItems[$scope.currentPage].length - 1; i >= 0; i--){
if($scope.pagedItems[$scope.currentPage][i].name == client.name){
$scope.pagedItems[$scope.currentPage].splice(i,1);
break;
}
}
}
This should be your search function (note creation of $scope.clientsCopy):
$scope.clientsCopy = angular.copy(data);
$scope.searchMatch = function (haystack, needle) {
if (!needle) {
return true;
}
return haystack.toLowerCase().indexOf(needle.toLowerCase()) !== -1;
};
// init the filtered items
$scope.search = function () {
$scope.filteredItems = $filter('filter')($scope.clientsCopy, function (item) {
for(var attr in item) {
if ($scope.searchMatch(item.name, $scope.query))
return true;
}
return false;
}
);
// take care of the sorting order
if ($scope.sortField !== '') {
$scope.filteredItems = $filter('orderBy')($scope.filteredItems, $scope.sortField, $scope.reverse);
}
$scope.currentPage = 0;
// now group by pages
$scope.groupToPages();
};
Plunker
You are trying to alter your $scope.clients which actually is empty because in your groupToPages function you set it to an empty array and repopulate or use it.
$scope.groupToPages = function () {
$scope.clients = [];
You can get it to work by removing items from your filteredItems array.
$scope.remove = function(client){
$scope.filteredItems.splice($scope.filteredItems.indexOf(client),1);
$scope.groupToPages();
}
http://plnkr.co/edit/z9NxW2FgYDlgLB20daO9?p=preview

Search in array of objects with object javascript

I have an array like this
var userdata = [
{"id":1,"gender":"M","first":"John","last":"Smith","city":"Seattle, WA","status":"Active"},
{"id":2,"gender":"F","first":"Kelly","last":"Ruth","city":"Dallas, TX","status":"Active"},
{"id":3,"gender":"M","first":"Jeff","last":"Stevenson","city":"Washington, D.C.","status":"Active"},
{"id":4,"gender":"F","first":"Jennifer","last":"Gill","city":"Seattle, WA","status":"Inactive"}
]
I need to filter this array on some conditions. The form of these conditions are like this.
var search_object = {gender:"M",city:"Seattle, WA"}
// Gender = M and city = 'Seattle, WA'
var search_object1 = {gender:"M"}
var search_object2 = {city:"Seattle, WA"}
// This is same as above
var search_array = {status:["Active","Inactive"]}
// Status Active or Inactive
var search_array = [{status:"Active"},{status:"Inactive"}]
// Same as above
var search_object1 = {gender:"F"}
var search_array = [{status:"Active"},{status:"Inactive"}]
//Gender = F and status = Active or Inactive
var search_object = {gender:"F"}
var search_array = [{status:["Active","Inactive"]}]
// same as above
I have tried looping but failed. Please help or suggest or provide some proper links to get help.
The following code covers all the cases you mentioned.
function search(searchObj, data) {
if(searchObj instanceof Array) {
return data.reduce(function(prev, current, index, array) {
return prev.concat(search(current, data));
}, []);
} else {
var results = data.filter(function(el) {
for(var prop in searchObj) {
if(searchObj[prop] instanceof Array) {
if(searchObj[prop].indexOf(el[prop]) == -1) {
return false;
}
} else
if(el[prop] !== searchObj[prop]) {
return false;
}
}
return true;
});
return results;
}
};
search(search_object, userdata);
Here is the working example in JSFiddle.
And here are some links to the functions I've used above:
Array.prototype.reduce()
Array.prototype.concat()
Array.prototype.filter()
Array.prototype.indexOf()
Just what RGraham said in the comments, you can use the filter function on arrays.
var search_object = {gender:"M",city:"Seattle, WA"};
var filtered = userdata.filter(function(obj){
return (obj.gender === search_object && obj.city === search_object.city)
});
filtered[0];//Array with objects that return true;

Properly update the source Option in bootstrap-typeahead.js

In the following demo, after inserting "Alaska" value,
the source is updated so that the autocomplete is not showing again Alaska value.
var newSource = this.source
.slice(0,pos)
.concat(this.source.slice(pos+1));
this.source = newSource;
Anyway if I remove Alaska from the textarea, the value Alaska should be displayed again in the source.
Any hints how to restore the source data if the user delete the data from the textarea?
My idea is to access the options `source option from
$('.typeahead').on('change', function () { })
Any hints?
P.S.:
I am using jquery and underscore
You should probably rather change your matcher function in order to test over the already selected states :
var tabPresentStates = this.query.split(','),
nbPresentStates = tabPresentStates.length;
for(var iState = 0; iState < nbPresentStates; iState++) {
if(item === tabPresentStates[iState].trim())
return false;
}
See this fiddle.
Instead of changing the source you can use the sorter to exclude the values you've already selected.
http://jsfiddle.net/BwDmM/71/
P.S. I'll probably include your code in next version of Jasny's extended Bootstrap http://jasny.github.com/bootstrap :)
!function(source) {
function extractor(query) {
var result = /([^,]+)$/.exec(query);
if(result && result[1])
return result[1].trim();
return '';
}
$('.typeahead').typeahead({
source: source,
updater: function(item) {
return this.$element.val().replace(/[^,]*$/,'')+item+',';
},
matcher: function (item) {
var tquery = extractor(this.query);
if(!tquery) return false;
return ~item.toLowerCase().indexOf(tquery)
},
highlighter: function (item) {
var query = extractor(this.query).replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, '\\$&')
return item.replace(new RegExp('(' + query + ')', 'ig'), function ($1, match) {
return '<strong>' + match + '</strong>'
})
},
sorter: function(items) {
var beginswith = []
, caseSensitive = []
, caseInsensitive = []
, existing = $.each(this.$element.val().split(','), function(i, val) { return val.trim() })
, item
while (item = items.shift()) {
if ($.inArray(item, existing) >= 0) continue;
if (!item.toLowerCase().indexOf(this.query.toLowerCase())) beginswith.push(item)
else if (~item.indexOf(this.query)) caseSensitive.push(item)
else caseInsensitive.push(item)
}
return beginswith.concat(caseSensitive, caseInsensitive)
}
});
}(["Alabama","Alaska","Arizona","Arkansas","California","Colorado","Connecticut","Delaware","Florida","Georgia","Hawaii","Idaho","Illinois","Indiana","Iowa","Kansas","Kentucky","Louisiana","Maine","Maryland","Massachusetts","Michigan","Minnesota","Mississippi","Missouri","Montana","Nebraska","Nevada","New Hampshire","New Jersey","New Mexico","New York","North Dakota","North Carolina","Ohio","Oklahoma","Oregon","Pennsylvania","Rhode Island","South Carolina","South Dakota","Tennessee","Texas","Utah","Vermont","Virginia","Washington","West Virginia","Wisconsin","Wyoming"]);
Agreed with #SamuelCaillerie's approach to use matcher - this is just using your extractor function. So:
updater: function(item) {
return this.$element.val().replace(/[^,]*$/,'')+item+',';
},
matcher: function (item) {
var previouslySelected = (this.query.indexOf(item) != -1);
var tquery = (previouslySelected ? "" : extractor(this.query));
if(!tquery) return false;
return ~item.toLowerCase().indexOf(tquery)
},

Categories