Filter filtered results function AngularJS ngrepeat - angularjs

I want to filter filtered results.
I have the following JSON
$scope.JSON = [
{
"Animal": "Horse",
"Status": "awake",
"LastUpdate": {
"$date": 1473248184519
}
},
{
"Animal": "Rabbit",
"Status": "awake",
"LastUpdate": {
"$date": 1473248194240
}
},
{
"Animal": "Rabbit",
"Status": "eating",
"LastUpdate": {
"$date": 1473249639255
}
},
{
"Animal": "Horse",
"Status": "eating",
"LastUpdate": {
"$date": 1473249652549
}
},
{
"Animal": "Horse",
"Status": "sleeping",
"LastUpdate": {
"$date": 1473249656338
}
}
]
and the following filter function
$scope.filtering = filtering;
function filtering(animals){
var temp = [].concat(animals)
var animalsExisting = []; // To keep track of animals already added
return temp.reverse().filter(function(animal){
var notAdded = animalsExisting.indexOf(animal.Animal) === -1;
if(notAdded) {
animalsExisting.push(animal.Animal);
}
return notAdded;
})
}
See also this plunkr:
https://plnkr.co/edit/OQVjB47bpS9fKubO2lum?p=preview
How do I filter the returned Array notAdded with the Status, e.g. I want to show only the last & "eating" animals => result: Rabbit?

First option - Slow, but easy
Easiest way - using native Angular filter
<tr ng-repeat="roll in filtering(JSON) | filter : { Status: 'eating' }">
Second option - quick
Because either Angular will invoke filtering(JSON) on each $digest (which occures pretty often) it would be much better if you render a static list, not the return result of the function.
What I suggest is you rewrite your code to something along the lines
$scope.filters = {
status: null
};
$scope.filteredAnimals = [];
$scope.getLastStatusesForEachAnimal = function() {
var map = {};
return [].concat($scope.JSON)
.reverse()
.filter(function(animal) {
return !map[animal.Animal] ? (map[animal.Animal] = true) : false;
});
};
$scope.filterAnimals = function() {
$scope.filteredAnimals = $scope.getLastStatusesForEachAnimal()
.filter(function(animal) {
return $scope.filters.status ? animal.Status === $scope.filters.status : true;
});
};
$scope.filterAnimals();
Third option - a tad slower, but more elegant
Write your own filter with caching, example
.filter('animalStatusFilter', function() {
var cache = {
params: {},
result: null
};
return function(animals, status, onlyLast) {
var params = {
animals: animals,
status: status,
onlyLast: onlyLast
};
if (validateCache(params)) {
return cache.result;
}
animals = onlyLast ? getLastStatusesForEachAnimal(animals) : animals;
if (status) {
animals = animals.filter(function(animal) {
return status ? animal.Status === status : true;
});
}
cache.params = params;
cache.result = animals;
return animals;
};
function validateCache(params) {
return params.animals === cache.params.animals &&
params.status === cache.params.status &&
params.onlyLast === cache.params.onlyLast
}
function getLastStatusesForEachAnimal(animals) {
var map = {};
return [].concat(animals)
.reverse()
.filter(function(animal) {
return !map[animal.Animal] ? (map[animal.Animal] = true) : false;
});
}
})

Related

nodejs how can i use multidimensional array and push values in it?

i am trying to create a new multidimensional array from the data i am getting from 3rd part API.
"output":[
{
"is_indb":false,
"name":"adam",
"tokens":29
},
{
"is_indb":true,
"name":"aaron",
"tokens":2,
},
{
"is_indb":false,
"name":"adam",
"tokens":3,
},
{
"is_indb":false,
"name":"axel",
"tokens":5,
},
{
"is_indb":false,
"name":"andy",
"tokens":5,
},
{
"is_indb":false,
"name":"bob",
"tokens":5,
},
{
"is_indb":false,
"name":"aldo",
"tokens":5,
},
{
"is_indb":false,
"name":"julia",
"tokens":5,
}
]
i would like to create a new array and fill it with data from response.
but i would like to do some pre checks like
take only those whose, is_indb = false
take only those whose, name starts with a
so the final array will be, all those whosse is_indb = true and name starts with a
var newaray = [[adam,29],[adam,3],[axel,5],[andy,5],[aldo,5]];
so far i have tried using _pluck and getting some weird outputs. i am able to get sible elements using _pluck but cant get multiple items.
i can think of logic like this
var newaray = [];
if( (_pluck(msg.output,'is_indb') == false && ((_pluck(msg.output,'name').substring(0, 1) == "a")){
newaray.push( [ _.pluck(msg.output, 'name') , _.pluck(msg.output, 'token')] );
}
Use filter and map:
var filteredOutput = output
.filter(function(elem) {
// The return statement should return true,
// if you want the element to pass into the new array.
return elem.is_indb === false && typeof elem.name === "string" && elem.name.indexOf('a') === 0;
})
.map(function(elem) {
return [elem.name, elem.tokens];
});
or with ES6:
let filteredOutput = output
.filter(elem => elem.is_indb === false && typeof elem.name === "string" && elem.name.indexOf('a') === 0)
.map(elem => [elem.name, elem.tokens])
with ES6 and using regex (inspired by Peter Grainger's answer, but also case insensitive):
let filteredOutput = output
.filter(elem => elem.is_indb === false && /^a/i.test(elem.name))
.map(elem => [elem.name, elem.tokens])
and by the way, what you posted is an array of objects, not a multidimensional array, which is an array of arrays.
You could use a filter then a map?
const output = [
{
"is_indb":false,
"name":"adam",
"tokens":29
},
{
"is_indb":true,
"name":"aaron",
"tokens":2,
},
{
"is_indb":false,
"name":"adam",
"tokens":3,
},
{
"is_indb":false,
"name":"axel",
"tokens":5,
},
{
"is_indb":false,
"name":"andy",
"tokens":5,
},
{
"is_indb":false,
"name":"bob",
"tokens":5,
},
{
"is_indb":false,
"name":"aldo",
"tokens":5,
},
{
"is_indb":false,
"name":"julia",
"tokens":5,
}
]
const transform = output.filter(value => /^a/.test(value.name) && !value.is_indb)
.map(value => [value.name, value.tokens])
console.log(transform)
You can use _.filter and get the output in this form
op = [{ obj1 } ,{obj2}];
but as you want to remove some keys also then you can use _.pick
var op = _.filter(ip , function(obj){
if(obj.is_indb == false && obj.name[0] == 'a'){
return true;
}
else{
return false;
}
})
//so now you have all the entries filtered out
var opAltered = _.pick(op,['name','tokens']);
//you will get this result
/*
opAltered = [
{
name : <something>,
tokens : <something>
},{
...
}
]
*/
or If you want array you can use this
opAltered = _.map(op,['name','tokens'];
I have used more code to make you understand properly you can reduce it once you understand Thanks.

How to write an if statement to check whether any item in array is greater than a specific value/date?

So, I have a function which should be executed in case that a if condition is true. I simply do not know how to implement it in a method. I have the following code:
Meteor.methods({
'popItems': function () {
var date = new Date().getTime();
if ( "check if this.userId && any item in the array 'itemIds' is $gt date" ) {
userManagement.update({
'_id': this.userId
}, {
$pop: {'itemIds': -1}
}
}
);
};
}
});
So, in case the if condition is true, the $pop function should be executed. In case false, it should not. I wrote this for the if clause, but it doesn't work:
if (this.userId && userManagement.find({
'itemIds': {$gt: date}})) {...$pop function...}
Meteor.methods({
'popItems': function () {
var date = new Date().getTime();
if (this.userId && userManagement.find({'itemIds':{ $gt: date}}).count() > 0 ) {
userManagement.update({
'_id': this.userId
}, {
$pop: {'itemIds': -1}
});
}
};
});
Include the query in the update operation as
Meteor.methods({
'popItems': function () {
var date = new Date();
userManagement.update(
{
'_id': this.userId,
'itemIds': { '$gt': date }
},
{ '$pop': { 'itemIds': -1 } }
);
}
});
I've made some assumptions in coming up with the above solution. The first one being itemIds is an array composed of just Date objects e.g.
itemIds: [
ISODate("2017-01-25T06:20:00.000Z"),
ISODate("2017-01-26T06:20:00.000Z"),
ISODate("2017-01-27T06:20:00.000Z"),
...
ISODate("2017-02-25T06:20:00.000Z")
]
The above query in the update operation can also be specified with an $and operator as:
Meteor.methods({
'popItems': function () {
var date = new Date();
userManagement.update(
{
'$and': [
{ '_id': this.userId },
{ 'itemIds': { '$gt': date } },
]
},
{ '$pop': { 'itemIds': -1 } }
);
}
});

How to get a single object by value in AngularJS Factory

How do I get a single object from the flights array? I am first calling the getFlights() function in one view, and then I want to use the getFlight(flightId) in a another view when going to the page for a specific flight.
When I call the getFlight(flightId) however, it contains no data.
angular.module('myApp.factories', []).
factory('flightFactory', function ($http) {
var flights = {};
return {
getFlights: function () {
return $http.get('app/data/flights.json').then(function (response) {
flights = response;
return flights;
});
},
getFlight: function (id) {
for (var i = 0; i < flights.length; i++) {
if (flights[i].flightId == id) {
return flights[i];
}
}
return null;
}
};
});
Here is how i get all the flights in the first controller
flightFactory.getFlights().then(function(response) {
$scope.flights = response.data;
});
Here is how I have been trying to get a single flight in the second controller
$scope.singleflight = flightFactory.getFlight($routeParams.flightID);
Here is the content of the JSON file
[
{
"date": "2016-02-18T00:19:11-08:00",
"numberOfSeats": 30,
"totalPrice": 610,
"flightId": "PR6674",
"travelTime": 422,
"destination": "South Sudan",
"origin": "Copenhagen"
},
{
"date": "2016-11-07T16:35:45-08:00",
"numberOfSeats": 87,
"totalPrice": 405,
"flightId": "ZN4890",
"travelTime": 196,
"destination": "Rwanda",
"origin": "Copenhagen"
},
{
"date": "2016-07-11T18:05:32-07:00",
"numberOfSeats": 36,
"totalPrice": 646,
"flightId": "AT1096",
"travelTime": 399,
"destination": "Equatorial Guinea",
"origin": "Copenhagen"
}
]
Help will be much appreciated!
#Jeffery Yu's works, but it exposes the flights array.
angular.module('myApp.factories', []).
factory('flightFactory', function ($http) {
var flights = {};
var _this = this;
return {
getFlights: function () {
return $http.get('app/data/flights.json').then(function (response) {
_this.flights = response;
return _this.flights;
});
},
getFlight: function (id) {
var flights = _this.flights;
for (var i = 0; i < flights.length; i++) {
if (flights[i].flightId == id) {
return flights[i];
}
}
return null;
}
};
});
this will hide the flights array from the user (no direct access). This is what #Anik Islam Abhi is talking about.
angular.module('myApp.factories', []).factory('flightFactory', function ($http) {
return {
flights:{},
getFlights: function () {
return $http.get('app/data/flights.json').then(function (response) {
this.flights = response;
return this.flights;
});
},
getFlight: function (id) {
for (var i = 0; i < flights.length; i++) {
if (this.flights[i].flightId == id) {
return this.flights[i];
}
}
return null;
}
};
});

Display the count of array in ng-repeat using angularjs

I have a JSON response which contains an array of data.
$scope.Load = function () {
$http.post(elasticSearchURL, { "aggs": { "By_type": { "terms": { "field": "Name" }, "aggs": { "By_color": { "terms": { "field": "Color" } } } } } }).
then(function (response) {
$scope.modelList = response;
}, function (response) {
});
}
I would like to display the count of each element in the JSON.
MyHtml Code
<div ng-repeat="fruits in modelList.data.hits.hits | unique : '_source.fruitsName' | orderBy:'_source.fruitsName' ">
{fruits._source.name.length}
</div>
My Json data will be like this
{"_id":"e-AB2","_version":1,"found":true, "_source":{"name":"Apple", "color":"red"}}},
{"_id":"e-AB2","_version":1,"found":true, "_source":{"name":"Apple", "color":"red"}}},
{"_id":"e-AB2","_version":1,"found":true, "_source":{"name":"Apple", "color":"red"}}}
It displays only the length of the fruitname "Apple" length:5. I want to display the array length of _source.name
I have tried {{$index.length}} but it showing the count as 1 since I am using "unique:" in the <div>
How it can be achieved in angularjs.
Thanks in advance
I think the easiest approach is create hashmap that contains the counts:
$scope.counts = {};
$http.post(elasticSearchURL, postData).then(function (response) {
$scope.modelList = response.data;
response.data.forEach(function (item) {
var source = item._source.name;
if (!$scope.counts.hasOwnProperty(source)) {
$scope.counts[source] = 0;
}
$scope.counts[source]++;
});
});
Then in the view you can use:
Count = {{counts[fruits._source.name]}}
$scope.counts will look like:
{ 'Apple':3, 'Orange':5}
What you probably want to do is map all the data to a different array in this same fashion and get rid of the unique filter since there is no way to do what you are asking directly in the view

Joining data between paths based on id using AngularFire

I am currently working on an app using firebase and angularJS (ionic). Basically this is a car management app, so you have people sharing their cars with others. I tried to structure the data as flat as possible to be efficient. My issue here is that if without problem I can display the list of the car_id of the different cars shared with the logged user, I can't find a way to display the list of cars shared with the user displaying the year and the model.
Thank you in advance for your help !
{
"rules": {
"users": {
".write": true,
"$uid": {
".read": "auth != null && auth.uid == $uid"
},
"cars": {
"car_id":true,
"role":true // Owner, borower...
}
},
"cars": {
"car_id":true,
"model":true,
"year":true
}
}
}
carapp.controller("carsController", function($scope, $firebaseObject, $ionicPopup, $ionicHistory) {
$ionicHistory.clearHistory();
$scope.list = function() {
frbAuth = frb.getAuth();
if(frbAuth) {
var userObject = $firebaseObject(frb.child("users/" + frbAuth.uid));
userObject.$bindTo($scope, "user");
$scope.cars = frb.child("cars");
}}
$scope.createCar = function() {
$ionicPopup.prompt({
model: 'Create a new car',
inputType: 'text'
})
.then(function(result) {
if(result !== "") {
var newCar = $scope.cars.push({
model: result
})
var newCarId = newCar.key();
$scope.user.cars.push({car_id: newCarId, role: "owner" });
} else {
console.log("Action not completed");
}
});
}
});
<div class="list">
<a ng-repeat="car in user.cars" >
<h2>{{car.car_id}}</h2> ----> works fine !
<h2>{{car.model}}</h2> ----> How to get this working ?
<h2>{{car.year}}</h2> ----> How to get this working ?
</a>
</div>
In the users/ path, begin by storing the list of cars by index, instead of in an array. So your structure would be:
{
"users": {
"kato": {
"cars": {
"DeLorean": true
}
}
},
"cars": {
"DeLorean": {
model: "DeLorean",
year: "1975"
}
}
}
To join this using AngularFire, you have several approaches available. An AngularFire-only solution might look like this, taking advantage of $extend:
app.factory('CarsByUser', function($firebaseArray) {
return $firebaseArray.$extend({
$$added: function(snap) {
return new Car(snap);
},
$$updated: function(snap) {
// nothing to do here; the value of the index is not used
},
$$removed: function(snap) {
this.$getRecord(snap.key()).destroy();
},
// these could be implemented in a manner consistent with the
// use case and above code, for simplicity, they are disabled here
$add: readOnly,
$save: readOnly
});
var carsRef = new Firebase(...).child('cars');
function Car(snap) {
// create a reference to the data for a specific car
this.$id = snap.key();
this.ref = carsRef.child(this.$id);
// listen for changes to the data
this.ref.on('value', this.updated, this);
}
Car.prototype.updated = function(snap) {
this.model = data.model;
this.year = data.year;
}
Car.prototype.destroy = function() {
this.ref.off('value', this.meta, this);
};
function readOnly() { throw new Error('This is a read only list'); }
});
app.controller('...', function($scope, CarsByUser, authData) {
// authenticate first, preferably with resolve
var ref = new Firebase(...).child(authData.uid);
$scope.cars = CarsByUser($scope);
});
For a more sophisticated and elegant approach, one could utilize NormalizedCollection and pass that ref into the AngularFire array:
app.controller('...', function($scope, $firebaseArray) {
var ref = new Firebase(...);
var nc = new Firebase.util.NormalizedCollection(
ref.child('users/' + authData.uid),
ref.child('cars')
)
.select('cars.model', 'cars.year')
.ref();
$scope.cars = $firebaseArray(nc);
});

Resources