I'm having trouble converting the following Lodash statement to something that works in an application that I inherited at work and am trying to fix bugs in. At the moment, we are having issues with only one device on our system returning when two devices have the same name and the following code seems to be the culprit as it would only return the first "true" value in an array:
var group = _.find(groupList, {id: id});
How would I successfully convert this to a statement that iterates over all objects in an array and then returns those objects? I've tried the following options to no avail:
var group = _.filter(groupList, {id: id});
and
var group = _.every(groupList, {id: id});
and
var group = _.forEach(groupList, {id: id})
return {id};
I know I am probably missing something in my syntax. Any help would be much appreciated. Running Lodash v3.7.0
Here's the rest of the code in the directive in case anyone is interested or sees something else I might be missing:
define(['./../_module'], function (directives) {
'use strict';
directives.directive('dmGroupedList', ['$compile', '$state', 'APP_CONSTANTS', function ($compile, $state, APP_CONSTANTS) {
return {
restrict: 'A',
scope: {
items: '=',
groupBy: '=',
actions: '=',
nonameGroupLabel: '='
},
templateUrl: function (elem, attrs) {
return attrs.templateUrl || 'views/shared-templates/grouped-list.html';
},
link: function (scope, element, attrs) {
scope.$watchGroup(['items', 'groupBy', 'nonameGroupLabel'], function () {
scope.groupList = [];
scope.groupedItems = {};
var actions = scope.actions[scope.groupBy];
_.forEach(scope.items, function (item) {
scope.handlers.getGroups(scope.groupList, item, scope.items, scope.groupBy, actions);
});
_.forEach(scope.groupList, function (group) {
var items = scope.groupedItems[group.id];
items = _.sortBy(items, function (item) {
return item.description;
});
scope.groupedItems[group.id] = items;
});
var groupsToSort = _.where(scope.groupList, {unassigned: false});
var unassignedGroups = _.sortBy(_.where(scope.groupList, {unassigned: true}), 'name');
scope.groupList = _.sortBy(groupsToSort, function (group) {
return group.name;
});
//adds unassigned groups to a new array via the javascript "push" method
if (angular.isDefined(unassignedGroups)) {
for (var i = 0; i < unassignedGroups.length; i++) {
scope.groupList.push(unassignedGroups[i]);
}
}
});
scope.handlers = {
getGroups: function (groupList, item, items, groupBy, actions) {
var group = item[groupBy];
if (_.isEmpty(group)) {
scope.handlers.addGroupToList(groupList, APP_CONSTANTS.DEVICE_NONE_NAME_MAPPING.NONE, items, groupBy, item, actions, scope);
}
else {
if (angular.isArray(group) || angular.isObject(group)) {
_.forEach(group, function (groupName) {
if (groupName == APP_CONSTANTS.ZERO) {
scope.handlers.addGroupToList(groupList, APP_CONSTANTS.DEVICE_NONE_NAME_MAPPING.NONE, items, groupBy, item, actions, scope);
return;
}
scope.handlers.addGroupToList(groupList, groupName, items, groupBy, item, actions, scope);
})
} else {
scope.handlers.addGroupToList(groupList, group, items, groupBy, item, actions, scope);
}
}
},
addGroupToList: function (groupList, groupId, items, groupBy, item, handlers, scope) {
var id = _.camelCase(groupId);
var group = _.find(groupList, {id: id});
//var group = _.forEach(groupList, {id: id})
//return {id};
if (!group) {
var name = '';
var unassigned = false;
var link = null;
if (groupId == APP_CONSTANTS.DEVICE_NONE_NAME_MAPPING.NONE || groupId == APP_CONSTANTS.DEVICE_NONE_NAME_MAPPING.NONE_PARENT_ID) {
if (groupId == APP_CONSTANTS.DEVICE_NONE_NAME_MAPPING.NONE_PARENT_ID) {
name = APP_CONSTANTS.DEVICE_NONE_NAME_MAPPING.NONE;
} else {
name = APP_CONSTANTS.DEVICE_NONE_NAME_MAPPING.UNASSIGNED;
}
unassigned = true;
} else {
link = handlers.getGroupLink(groupId);
name = handlers.getGroupName(groupId, items);
}
group = {id: id, name: name, unassigned: unassigned, link: link};
groupList.push(group);
}
scope.groupedItems[group.id] = scope.groupedItems[group.id] || [];
if (angular.isDefined(handlers.processingGroup)) {
handlers.processingGroup(group, groupList, groupId, items, groupBy, item, handlers, scope);
} else {
scope.groupedItems[group.id].push({
description: handlers.getItemDescription(item),
link: handlers.getItemLink(item)
})
}
}
};
}
};
}]);
});
You can just use filter:
var group = groupList.filter((group) => group.id === id);
EDIT: to return only the element, and not an array, when there is only one match, you can do the following:
var checkSingle = (groups) => groups.length === 1 ? groups[0] : groups;
var group = checkSingle(groupList.filter((group) => group.id === id));
You can _(groupList).groupBy('id').get(id):
var groupList = [
{ id: 1, name: 'site' },
{ id: 2, name: 'test' },
{ id: 2, name: 'prod' },
{ id: 3, name: 'dev' },
{ id: 4, name: 'back' },
{ id: 4, name: 'front' },
{ id: 5, name: 'sprint' }
];
console.log(_(groupList).groupBy('id').get(2));
console.log(_(groupList).groupBy('id').get(3));
console.log(_(groupList).groupBy('id').get(4));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>
Related
I have data like this one below
let data = [
{
name: 'basic',
to: 'aaa',
subMenus: [
{
name: 'general conc',
to: 'geneal',
},
{
name: 'example view',
to: 'example',
},
{
name: 'fancy',
to: 'bbb',
innerSubMenus: [
{
name: 'adding',
to: 'add',
},
{
name: 'getting',
to: 'get',
},
]
}
]
}
]
I need to filter data based on name (in main, subMenus, and innerSubMenus)
Here is the piece of code
function deepFilter(inputText){
data.filter(items => items.name.toLowerCase().includes(inputText.toLowerCase()))
}
As you can see, the function filters the first prop (name --> basic in this case when inputText = 'basic', doesn't work when inputText = 'general conc') but I want to be able to filter names in subMenus and innerSubMenus as well. Please, guide me on how to do it. Thanks
expected outputs:
deepFilter('basic') -> true // only this part is covered by my implementation
deepFilter('general conc') -> true
deepFilter('adding') -> true
deepFilter('ffff') -> false //since there is not name with value of 'ffff' in data
I think this should work well.
function deepFilter(inputText, datas) {
return datas.filter(data => {
function checkInsideObj(object, inputText) {
for (let value of Object.values(object)) {
if (object.name && object.name.toLowerCase() === inputText.toLowerCase()) {
return true;
}
if (Array.isArray(value)) {
return value.some(item => {
return checkInsideObj(item, inputText)
}
)
}
}
return false;
}
return checkInsideObj(data, inputText)
}
)
}
deepFilter("input", data)
I encountered a question where I have to refactor the code below that has several errors in it:
function thingsToBuy() {
var list = [
"milk",
"bread",
"bananas"
];
return {
removeItem: function(name) {
list.filter(item => item === name);
},
addItem: function() {
return list.push();
},
getList: function(list) {
return list;
}
};
}
So far, I have this:
function thingsToBuy() {
let list = [
"milk",
"bread",
"bananas"
];
return {
removeItem: function(name) {
for(let i = 0; i < list.length; i++) {
if(list[i] === name)
return list.splice(i, 1)
}
},
addItem: function(item) {
list.push(item);
},
getList: function() {
return list;
}
};
}
Am I missing anything or have I implemented anything wrong? Any feedback would be much appreciated!
The solution:
function thingsToBuy() {
var list = [
"milk",
"bread",
"bananas"
];
return {
removeItem: function(name) {
list = list.filter(item => item !== name);
return list;
},
addItem: function(item) {
list.push(item);
return list;
},
getList: function() {
return list;
}
};
}
This is a basic exercise to see if you understand how JS works.
The function gives you some methods to mutate the list variable. You should read more about Array.filter, Array.push to see their input and output.
I am trying to order a list in a ng-repeat. What i want to do is set the order by hand, so i can say players first, coach second and waterboy last in the list. This is where i got but now got stuck with filters (kinda new to me):
$scope.Team= [{
Name: "Saras"
Role: "coach"
},{
Name: "Arya"
Role: "player"
},{
Name: "Adam"
Role: "waterboy"
},{
Name: "Theo"
Role: "player"
},{
Name: "Mark"
Role: "player"
},{
Name: "Oscar"
Role: "player"
},{
Name: "Tom"
Role: "player"
},{
Name: "Gus"
Role: "coach"
}];
<div ng-repeat="person in Team | orderBy:Role>
<div>{{person.Name}}</div>
</div>
i would like to orderBy 'Role' this way, where i can set the order
'player', 'coach', 'waterboy'
with the filter below i am able to set the order correct and add the label of the Role (coach, player etc.), however the labels sometimes get repeated. How could i fix that?
<div ng-repeat="person in Team | myOrder:'Role'>
<h2 ng-show="item.Role_CHANGED">{{item.Role}}</h2>
<p>{{person.Name}}</p>
</div>
app.filter('myOrder', function () {
return function (list, group_by) {
function CustomOrder(item) {
switch (item) {
case 'coach':
return 2;
case 'player':
return 1;
case 'waterboy':
return 3;
}
}
var filtered = [];
var prev_item = null;
var group_changed = false;
var new_field = group_by + '_CHANGED';
angular.forEach(list, function (item) {
group_changed = false;
if (prev_item !== null) {
if (prev_item[group_by] !== item[group_by]) {
group_changed = true;
}
} else {
group_changed = true;
}
if (group_changed) {
item[new_field] = true;
} else {
item[new_field] = false;
}
filtered.push(item);
prev_item = item;
});
filtered.sort(function (a, b) {
return (CustomOrder(a.Role) > CustomOrder(b.Role) ? 1 : -1);
});
return filtered;
};
})
try this example :
html :
<div class="test" ng-controller="Ctrl">
<div ng-repeat="division in divisions | orderBy:['group','sub']">{{division.group}}-{{division.sub}}</div>
<div>
js :
var app = angular.module('app', []);
function Ctrl($scope) {
$scope.divisions = [{'group':1,'sub':1}, {'group':2,'sub':10}, {'group':1,'sub':2},{'group':1,'sub':20},{'group':2,'sub':1},{'group':2,'sub':11}];
}
alright what i did to solve this is that i moved the sort function to the front when i GET the data from the JSON repsonse.
$http({
method: 'Get',
url: "https://xxxxxxx.azurewebsites.net/api/team/" + id_company
})
.success(function (data) {
var neworder = [];
function CustomOrder(item) {
switch (item) {
case 'player':
return 1;
case 'coach':
return 2;
case 'waterboy':
return 3;
}
}
angular.forEach(data, function (item) {
neworder.push(item);
});
neworder.sort(function (a, b) {
return (CustomOrder(a.Role) > CustomOrder(b.Role) ? 1 : -1);
});
$scope.Team = neworder;
});
after i just run the filter without the sort function;
<div ng-repeat="person in Team | myOrder:'Role'>
<h2 ng-show="item.Role_CHANGED">{{item.Role}}</h2>
<p>{{person.Name}}</p>
</div>
app.filter('myOrder', function () {
return function (list, group_by) {
var filtered = [];
var prev_item = null;
var group_changed = false;
var new_field = group_by + '_CHANGED';
angular.forEach(list, function (item) {
group_changed = false;
if (prev_item !== null) {
if (prev_item[group_by] !== item[group_by]) {
group_changed = true;
}
} else {
group_changed = true;
}
if (group_changed) {
item[new_field] = true;
} else {
item[new_field] = false;
}
filtered.push(item);
prev_item = item;
});
return filtered;
};
})
it's a bit of a work-around but it did the job
just to be complete, if you just want to sort the ng-repeat in a filter and no need for the lables. you can just do this:
<div ng-repeat="person in Team | Sorter:'Role'>
<p>{{person.Name}}</p>
</div>
app.filter('Sorter', function () {
function CustomOrder(item) {
switch (item) {
case 'player':
return 1;
case 'coach':
return 2;
case 'waterboy':
return 3;
}
}
return function (items, field) {
var filtered = [];
angular.forEach(items, function (item) {
filtered.push(item);
});
filtered.sort(function (a, b) {
return (CustomOrder(a.Role) > CustomOrder(b.Role) ? 1 : -1);
});
return filtered;
};
});
I have two table and i want to append second table data in $scope.notiData and how can i remove ng repeat data if i click remove symbol X. I have some code but it is not working.please help anyone
http://jsfiddle.net/A6bt3/118/
var app = angular.module('myApp', []);
function checkBoxCtrl($scope) {
$scope.tableOne = [{
firstname: 'robert',
value: 'a'
}, {
firstname: 'raman',
value: 'b'
}, {
firstname: 'kavi',
value: 'c'
}, {
firstname: 'rorank',
value: 'd'
}
];
$scope.tableTwo = [];//the table to be submitted
function removeitems(tableRef) { //revmove items from tableRef
var i;
for (i = tableRef.length - 1; i >= 0; i -= 1) {
if (tableRef[i].checked) {
tableRef.splice(i, 1);
}
}
}
$scope.btnRight = function () {
//Loop through tableone
$scope.tableOne.forEach(function (item, i) {
// if item is checked add to tabletwo
if (item.checked) {
$scope.tableTwo.push(item);
}
})
removeitems($scope.tableOne);
}
$scope.btnAllRight = function () {
$scope.tableOne.forEach(function (item, i) {
item.checked = true;
$scope.tableTwo.push(item);
})
removeitems($scope.tableOne);
}
$scope.btnLeft = function () {
$scope.tableTwo.forEach(function (item, i) {
if (item.checked) {
$scope.tableOne.push(item);
}
})
removeitems($scope.tableTwo);
}
$scope.btnAllLeft = function () {
$scope.tableTwo.forEach(function (item, i) {
item.checked = true;
$scope.tableOne.push(item);
})
removeitems($scope.tableTwo);
}
$scope.done = function () {
//alert(angular.toJson($scope.tableTwo));
$scope.notiData = $scope.tableTwo;
}
$scope.removeRow = function () {
}
};
do it like that :
$scope.removeRow = function (item) {
var index = $scope.notiData.indexOf(item);
$scope.notiData.splice(index, 1);
}
and to merge the arrays:
$scope.done = function () {
angular.extend($scope.notiData, $scope.tableTwo);
}
dont forget to initialize notiData :
$scope.notiData = [];
to resolve the problem of redirecting remove the target="_blank" and change href:
<a ng-repeat="data in notiData" class="emailButton" href="#">{{data.firstname}}<div class="" ng-click="removeRow()">X</div></a>
http://jsfiddle.net/A6bt3/122/
html element
<a ng-repeat="data in notiData" class="emailButton">{{data.firstname}}
<div class="" ng-click="removeRow($index)">X</div>
</a>
remove function
$scope.removeRow = function (index) {
$scope.notiData.splice(index,1);
}
pass the index of the data element in the removeRow() function from the view.
<a ng-repeat="data in notiData" class="emailButton" href="#">{{data.firstname}}
<div class="" ng-click="removeRow($index)">X</div>
</a>
and in controller filter the passed index element from the list.
$scope.removeRow = function (index) {
$scope.notiData = $scope.notiData.filter(function(elem){
return elem !== $scope.notiData[index]
})
}
fiddle : http://jsfiddle.net/w5upkawt/
Sorry for beginner question , how to make the factory have another factory?I need the foods data have link to restaurant (like one to many relationship).
This is my code:
.factory('Foods', function() {
var foods = [{
id: 1,
name: 'Food 1',
price: '$12',
category:'Appetizer',
imageSrc: 'img/new_food1.jpg',
restaurant:Restaurants.get(1),
}];
return {
all: function() {
return foods;
},
remove: function(food) {
foods.splice(foods.indexOf(food), 1);
},
get: function(foodId) {
for (var i = 0; i < foods.length; i++) {
if (foods[i].id === parseInt(foodId)) {
return foods[i];
}
}
return null;
}
};
})
And this is my factory for restaurant code:
.factory('Restaurants', function() {
var restaurants = [{
id: 1,
name: 'Restaurant 1',
imageSrc: 'img/resto1.jpg'
}];
return {
all: function() {
return restaurants;
},
remove: function(restaurant) {
restaurants.splice(restaurants.indexOf(restaurant), 1);
},
get: function(restaurantId) {
for (var i = 0; i < restaurants.length; i++) {
if (restaurants[i].id === parseInt(restaurantId)) {
return restaurants[i];
}
}
return null;
}
};
});
But it is makes the data error. How to make the array have object value (Restaurant)? Thanks...
Services can require other services. You can use the Angular auto injection by using a parameter with the name of your service :
.factory('Foods', function(Restaurants) {
// ...
Restaurants.get(42);
// ...
});