Refactoring methods in Javascript (adding / deleting items from an array) - javascript

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.

Related

AngularJS filter | orderBy with custom order

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;
};
});

How to parse JSON having nested arrays in javascript or jquery

I want to parse JSON like below
{
"nodeId":3892718504,
"root":true,
"subs":[
{
"nodeId":3892717286
},
{
"nodeId":3892716092,
"subs":[
{
"nodeId":3892715856,
"subs":[
{
"nodeId":3892718592,
"subs":[
{
"nodeId":3892717580
}
]
}
]
}
]
},
{
"nodeId":3892717497
}
]
}
Each node can have subs and those subs can have nodes that can have their own subs. all I want is an array having all nodeId, how can I parse this JSON such that an array called nodes_list is populated with all nodeId.
I can use javascript or jquery.
I'm trying the following approach to get an array of nodeId
jQuery.each(response.topology, function(i,obj) {
if(i == "nodeId") {
node_list.push(obj)
}
if(i == "subs"){
jQuery.each(i, function(key,value) {
if(i == "nodeId") {
node_list.push(obj)
}
}
}
});
I just need a little hint on how it can be in an iterative manner.
This can be done with function generators.
Perhaps not the most enjoyable approach, but I'm pretty sure the other solutions will already imply using other ways, so here is a solution using generators.
PS: Beware of browser support: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/yield
const input = {
"nodeId":3892718504,
"root":true,
"subs":[
{
"nodeId":3892717286
},
{
"nodeId":3892716092,
"subs":[
{
"nodeId":3892715856,
"subs":[
{
"nodeId":3892718592,
"subs":[
{
"nodeId":3892717580
}
]
}
]
}
]
},
{
"nodeId":3892717497
}
]
};
function* nodeLookup(obj) {
if (obj.nodeId) yield obj.nodeId;
if (obj.subs) for (var i = 0; i < obj.subs.length; i++) yield *nodeLookup(obj.subs[i]);
};
const node_ids = [...nodeLookup(input)];
console.log(node_ids);
Just use recursion to iterate over subs
var nodeIds = [];
if (data.nodeId) nodeIds.push(data.nodeId);
function fetchNodeIds (subs) {
if (!subs.length) return cb([]);
var abc = [];
subs.forEach(function (sub) {
abc.push(sub.nodeId);
if (sub.subs && sub.subs.length) abc = abc.concat(fetchNodeIds(sub.subs))
});
return abc;
}
nodeIds = nodeIds.concat(fetchNodeIds(data.subs));
console.log('--All nodeIds--', nodeIds)
It's straightforward to do recursively:
const gatherIds = ({nodeId, subs}, results = []) => subs
? [...results, nodeId, ...(subs .flatMap (sub => gatherIds (sub, results) ))]
: [...results, nodeId]
const response = {"nodeId": 3892718504, "root": true, "subs": [{"nodeId": 3892717286}, {"nodeId": 3892716092, "subs": [{"nodeId": 3892715856, "subs": [{"nodeId": 3892718592, "subs": [{"nodeId": 3892717580}]}]}]}, {"nodeId": 3892717497}]}
console .log (
gatherIds (response)
)
If your target environments don't support flatmap, it's easy enough to shim.

Filter an array which is a property of an object in an immutable way

How do I filter an array which is a property of an object in an immutable way?
For ex.
public transform(contactGroups: ContactGroup[], searchText: string): ContactGroup[] {
if (!contactGroups) {
return [];
}
if (searchText === undefined) {
return contactGroups;
}
return contactGroups.filter((contactGroup: ContactGroup) => {
return contactGroup.contacts.filter((contact: Contact) => {
return contact.displayName && contact.displayName.toLowerCase().includes(searchText.toLowerCase())
}).length > 0;
});
}
In above example, contactGroup.contacts contains all the items from the array but not the filtered result due object reference.
Any help would be appreciated.
Thank you.
function transform(contactGroups, searchText) {
if (!contactGroups) {
return [];
}
if (searchText === undefined) {
return contactGroups;
}
return contactGroups.filter(function (contactGroup) {
return contactGroup.contacts.filter(function (contact) {
return (contact.displayName && contact.displayName.toLowerCase().includes(searchText.toLowerCase()));
}).length > 0;
});
};
var contactGroups = [{
"letter":"S",
"contacts":[
{
"id":"173",
"rawId":null,
"displayName":"sam",
"name":{
"givenName":"sam",
"formatted":"sam"
},
"nickname":null,
"phoneNumbers":null,
"emails":[
{
"id":"955",
"pref":false,
"value":"sam#xyz.com",
"type":"other"
}
],
"addresses":null,
"ims":null,
"organizations":null,
"birthday":null,
"note":"",
"photos":null,
"categories":null,
"urls":null
},
{
"id":"1717",
"rawId":null,
"displayName":"Sat33",
"name":{
"givenName":"Sat33",
"formatted":"Sat33 "
},
"nickname":null,
"phoneNumbers":[
{
"id":"5521",
"pref":false,
"value":"1133",
"type":"work"
}
],
"emails":null,
"addresses":null,
"ims":null,
"organizations":null,
"birthday":null,
"note":null,
"photos":null,
"categories":null,
"urls":null
},
{
"id":"1712",
"rawId":null,
"displayName":"Server1234",
"name":{
"givenName":"Server1234",
"formatted":"Server1234 "
},
"nickname":null,
"phoneNumbers":[
{
"id":"5509",
"pref":false,
"value":"1234",
"type":"mobile"
}
],
"emails":null,
"addresses":null,
"ims":null,
"organizations":null,
"birthday":null,
"note":null,
"photos":null,
"categories":null,
"urls":null
}
]
}]
console.log(transform(contactGroups, 'ver'))
It should return contactGroup with only contact object 'Server1234' since the searched string is 'ver' but it still returns contactGroup with all contact object.
you can try this solution
class ContactGroup {
contacts: Contact[]
}
class Contact {
displayName: string
}
function transform(contactGroups: ContactGroup[], searchText: string): ContactGroup[] {
if (!contactGroups) {
return [];
}
if (searchText === undefined) {
return contactGroups;
}
return contactGroups.map((contactGroup: ContactGroup) => {
return {
...contactGroup,
contacts: contactGroup.contacts.filter((contact: Contact) => {
return contact.displayName && contact.displayName.toLowerCase().includes(searchText.toLowerCase())
})
}
}).filter((contactGroup: ContactGroup) => {
return contactGroup.contacts.length > 0
})
}
You can empty the passed array and push filtered elements into it. Here is a simplified version of your code:
function transform(contactGroups, searchText) {
var filtered = contactGroups.filter(e => e.name.includes(searchText));
contactGroups.splice(0, contactGroups.length);
contactGroups.push(...filtered);
return filtered; // <-- if still needed
}
var contacts = [{name: 'abc'}, {name: 'abcc'}, {name: 'xyz'}];
transform(contacts, 'abc');
console.log(contacts);

Lodash/JS - Convert _.find statement to _.forEach

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>

get Json Key based on Json Value of an Object which resides in an Json Array

How to get key of a json which resides within a array based on value, say if the value as ValueB it should return MainB
var app = angular.module('myApp', []);
app.controller('ArrayController', function ($scope) {
$scope.myDatas = [
{
"Test1": "Value1",
"MainA": ""
},
{
"Test1": "Value2",
"MainA": "ValueB"
},
{
"Test1": "",
"MainA": "ValueC"
}
];
$scope.getJsonKey = function(jsonArray, jsonValue)
{
angular.forEach(jsonArray, function(value, index)
{
if (value.Test1 === jsonValue)
{
return "Test1";
}
if (value.MainA === jsonValue)
{
return "MainA";
}
});
};
console.log($scope.getJsonKey($scope.myDatas, "ValueB"));
});
Can anyone please tell me some solution for this
Here is a small function that should do what you want, as long as the values are unique:
var arr=[
{
"Test1": "Value1",
"MainA": "ValueA"
},
{
"Test2": "Value2",
"MainB": "ValueB"
},
{
"Test3": "Value3",
"MainC": "ValueC"
}
]
function getKey(arr, val){
for(var i=0;i<arr.length;i++){
var item= arr[i];
for(var key in item){
if(val === item[key]) return key;
}
}
return null; // not found
}
console.log(getKey(arr, 'ValueB')) // MainB
You haven't shared too much code here, so I'm not sure what the best solution would be.
However, to simply answer you question, you could use this example:
Convert JSON to a js object via $.parseJSON(), and then find your key:
function getKeyInArray(arr, val){
for(var i=0, len=arr.length;i<len;i++){
var obj = arr[i];
for(var key in obj){
if(obj[key] == val)
return key;
}
}
return null;
}
view example:
http://jsfiddle.net/wc3mhg8u/21/
Try this
$scope.getJsonKey = function(jsonArray, jsonValue)
{
angular.forEach(jsonArray, function(value, index)
{
for(var key in value) {
if (value === jsonValue)
{
return key
}
}
});
};
console.log($scope.getJsonKey($scope.myDatas, "ValueB"));

Categories

Resources