Removing element from array in class method - javascript

I was working on a hackerrank problem and test cases showed that something was wrong with my 'remove' method. I always got undefined instead of true/false.
I know splice returns array of deleted elements from an array. When I console.log inside map, it looked like everything was fine when I was deleting first element (I was getting what I expected except true/false). But when 'name' I am deleting is not first element, I didn't get what I expected to get. Could you help me fix this? And of course, I never get true or false...
class StaffList {
constructor() {
this.members = [];
}
add(name, age) {
if (age > 20) {
this.members.push(name)
} else {
throw new Error("Staff member age must be greater than 20")
}
}
remove(name) {
this.members.map((item, index) => {
if(this.members.includes(name)) {
console.log(name)
let removed = this.members.splice(item, 1);
console.log(removed)
return true;
} else {
return false;
}
})
}
getSize() {
return this.members.length;
}
}
let i = new StaffList;
i.add('michelle', 25)
i.add('john', 30);
i.add('michael', 30);
i.add('jane', 26);
i.remove('john');
console.log(i);

Your return statements are wrapped within .map() (which you misuse in this particular case, so you, essentially, build the array of true/false), but your remove method does not return anything.
Instead, I would suggest something, like that:
remove(name){
const matchIdx = this.members.indexOf(name)
if(matchIdx === -1){
return false
} else {
this.members.splice(matchIdx, 1)
return true
}
}

In the remove method, you're using map with the array, which runs the function you give as argument for each array element. But I believe you don't want to do that.
Using the example you have bellow, basically what you do there is check if the array contains the name 'john', and if so, you delete the first item that appears in the array (which would be 'michelle'). This happens because the map function will run for every element, starting on the first one, and then you use that item to be removed from the array. After that, it returns the function, and no other elements get removed.
So my suggestion is just getting rid of the map function and running its callback code directly in the remove method (you would need to get the name's index in the array to use the splice method).

It is not clear why you need to use iterative logic to remove an item. You can simply use findIndex() to get the position of the member in the array. If the index is not -1, then you can use Array.prototype.slice(index, 1) to remove it. See proof-of-concept example below:
class StaffList {
constructor() {
this.members = [];
}
add(name, age) {
if (age > 20) {
this.members.push(name)
} else {
throw new Error("Staff member age must be greater than 20")
}
}
remove(name) {
const index = this.members.findIndex(x => x === name);
if (index !== -1) {
this.members.splice(index, 1);
}
}
getSize() {
return this.members.length;
}
}
let i = new StaffList;
i.add('michelle', 25)
i.add('john', 30);
i.add('michael', 30);
i.add('jane', 26);
i.remove('john');
console.log(i);

Use a filter method instead of map it's more elegant and you can return the rest of the array as well instead of true or false unless the problem you're working on requires true of false specifically.
You could write something like this:
remove(name) {
if (!this.members.some(el => el === name)) return false;
this.members = this.members.filter(item => item !== name);
return true;
}

Related

hasOwnProperty() is only checking if a certain property exists in a JSON, but doesn't return anything if it doesn't

I keep trying different methods to check if this JSON contains "attributes." In this way I can determine if the given coordinates are outside of wetlands. If they are in wetlands, "attributes" will exist in the JSON. If they aren't in wetlands, 'attributes' won't be in the JSON.
When I run this function, I am only getting TRUE - when I type in coordinates that are in a wetland (try 43.088 instead, in the JSON url, which returns true).
However I want FALSE for the given url. For some reason when I do console.log("FALSE"), this doesn't appear or return in the console at all if hasOwnProperty('attributes') == false.
Am I missing something?
function(GetData) {
fetch('https://www.fws.gov/wetlandsmapservice/rest/services/Wetlands/MapServer/0/query?where=&text=&objectIds=&time=&geometry=-88.305%2C43.060&geometryType=esriGeometryPoint&inSR=4326&spatialRel=esriSpatialRelWithin&relationParam=&outFields=WETLAND_TYPE&returnGeometry=false&returnTrueCurves=false&maxAllowableOffset=&geometryPrecision=&outSR=&returnIdsOnly=false&returnCountOnly=false&orderByFields=&groupByFieldsForStatistics=&outStatistics=&returnZ=false&returnM=false&gdbVersion=&returnDistinctValues=false&resultOffset=&resultRecordCount=&queryByDistance=&returnExtentsOnly=false&datumTransformation=&parameterValues=&rangeValues=&f=pjson&__ncforminfo=qCOZOO8Kyr4uogGcKvxkzzuK7gmavd4CxwTAkdbAsF2_aT4eeNbB0NpLwCYwiAJSf1ZHqY3CKVZ3osgMevhYGQrqRUQZej5oHaSmnSIaiZZb469Cexv-zqqmgYMuFJAAzrcRxvKXPBz9VnYPnMrM6kBNhO-cz6yK_w5T1mqNu_VXSbjBSihVf4_mlUBSVb9yf4C8scYXWm9Iak2Nfn1dtJACNUHLBHSElLvc1wxFMO2eUWNsD3qpCk3kAcRyYftuFU86n7THyk2IvkIUpxNmDHRxmmbgSYvPLMkl8t41Jzjp_bntkIyOWB0u8cQU2VsfASFUdznRkvrvYrQxgR8eyvsPq5oV_ZoPSksVCew6xev0K_TV2NU-kjojYpowMVXpZtCX9P-Q_7m8ywt2PyLPhEVgQB12ji1S7G5FRzIt6E0SDoXMY1vqQvPtedaNYbBCazXgs05L9DFKdtvrwmQVCeLmpBTduIhF9Sk4kozMnFX6GOANrZJMCI9AssN0DjrhlZkgDVw0l1flF44Zli927CXGTQ-oUpwsn7PPypVkN2iDJf-nz9XNbj82sv1c6B5s5UZVwiOp8VHJfZSDJ8BAYR4z_oONT2JwbVSKKlFKeN72f-Y6EejcB9wPKmn5kYjv7CKkRyIIv4F4cqVWxLK9x33uvEDMTvxX')
.then(function(response) {
return response.json();
})
.then(function(data) {
appendData3(data);
})
.catch(function(err) {
console.log('error: ' + err);
});
function appendData3(data) {
for (let obj of data['features']) {
if (obj.hasOwnProperty('attributes') == false) {
console.log("FALSE");
} else {
console.log("TRUE");
}
}
}
};
The issue is that in the response data['features'] is empty. When iterating over an empty array, nothing within the for...of loop is executed.
const emptyArray = [];
for (const item of emptyArray) {
// body is never executed...
}
If just checking the presence of an item within data['features'] is enough, you could use the length of the array.
function appendData3(data) {
if (data.features.length > 0) {
console.log("TRUE");
} else {
console.log("FALSE");
}
}
To check if one of the elements has the property "attributes" you could use some():
function appendData3(data) {
if (data.features.some(item => item.hasOwnProperty("attributes"))) {
console.log("TRUE");
} else {
console.log("FALSE");
}
}
If you're just trying to find out if a specific point is within one of the wetlands polygons, you could let the server to do the hard job and simplify your request. For example, ask for count.
See returnCountOnly at https://developers.arcgis.com/rest/services-reference/enterprise/query-feature-service-layer-.htm
https://www.fws.gov/wetlandsmapservice/rest/services/Wetlands/MapServer/0/query?geometry=-88.305%2C43.060&geometryType=esriGeometryPoint&inSR=4326&spatialRel=esriSpatialRelWithin&returnCountOnly=true&f=pjson
https://www.fws.gov/wetlandsmapservice/rest/services/Wetlands/MapServer/0/query?geometry=-88.305%2C43.088&geometryType=esriGeometryPoint&inSR=4326&spatialRel=esriSpatialRelWithin&returnCountOnly=true&f=pjson
I tested your code and this is the problem. When the coordinates are outside the wetlands, the features array is empty, that means nothing happen in your for loop. So do this instead of checking directly inside of your for loop
function appendData3(data) {
// Here we check if features is empty by checking it's length
if (data['features'].length == 0) {
console.log("FALSE")
}
for (let obj of data['features']) {
console.log("TRUE");
}
}
I also see that your for loop is only getting one object every time so instea of doing a for loop, just do it like this:
function appendData3(data) {
var obj = data['features'][0]
if(obj) {
console.log('TRUE')
} else {
console.log('FALSE')
}
}
As you can see, I did it even easier this time, just by getting the first object of features and checking if it exist.
Also, small tip: when you want to check if a condition is false, don't use == false, just put an exclamation mark at the beginning of the if statement. Like that:
if(!obj.hasOwnProperty('attributes')) { 
// Code here will be executed if the condition is false
} else {
// Code here will be executed if the condition is true
}
I hope this help you fixing your problem.
Have a nice day :)

Conditional filter function

I have a little problem with my filter function. I have a data structure as shown in the image below.
As you can see, I have an array of objects named bridals and it keeps another array of objects named plans. So inside that I am trying to filter people.
Here is the filter function.
export function peopleFilter(bridals, people){
if (people.length === 0) {
return bridals;
} else {
bridals.forEach(bridal => {
bridal.plans.filter((item) => {
if (item.people === people) {
return bridals;
}
})
})
return bridals;
}
}
The function peopleFilter() should filter plans with selected value only and return with bridals, but it returns nothing. No error is shown as well.
So I tried something like below.
bridals.forEach(bridal => {
bridal.plans.slice().reverse().forEach((item, index, object) => {
if (item.people !== people) {
bridal.plans.splice(object.length - 1 - index, 1)
}
})
})
return bridals;
This above code is doing what I want. But there is one problem. So in the end when I select no value, it should display all plans. But it doesn't because I already removed the plans using splice() every time I select some value.
So I am stuck about this. How can I fix it?
In this case it might be easier to iterate over your bridal array with a map. Within the map, filter plans for each bridal, then return a new obj, which can be accomplished with spread syntax.
const filterFunc = (bridals, people) => {
return bridals.map(bridal => {
const filteredPlans = bridal.plans.filter(plan => plan.people === people);
return { ...bridal, plans: filteredPlans };
})
}
Let's say you want to know the people count for all bridals and plans:
var total = 0;
bridals.forEach(function(b){
b.plans.forEach(function(o){
total += o.people;
});
});
Of course, I can't really tell what you're trying to do. I could write an entire API for this.
You need to return a boolean value inside the filter() instead of return bridals;. Additionally, this returned array needs to be reassigned to bridal array as well.
export function peopleFilter(bridals, people) {
if (people.length === 0) {
return bridals;
} else {
bridals.forEach(bridal => {
bridal = bridal.plans.filter((item) => {
return item.people === people;
})
})
return bridals;
}
}

How to pass an instance of an array to a function

I have a checkbox group that I want to get all checked items. I am trying to pass an Array to a function so I can get all checked items but it's not working.
checkedCategory: Array<number>;
contains(checkedArr: Array<number>, id: number): boolean {
if (checkedArr instanceof Array) {
return checkedArr.indexOf(id) > -1;
} else if (!!checkedArr) {
return checkedArr === id;
}
return false;
}
private add(checkedArr: Array<number>, id: number) {
if (!this.contains(checkedArr, id)) {
console.log('add: ' + checkedArr);
if (checkedArr instanceof Array) {
checkedArr.push(id);
} else {
checkedArr = [id];
}
}
}
private remove(checkedArr: Array<number>, id: number) {
const index = checkedArr.indexOf(id);
if (!checkedArr || index < 0) {
return;
}
checkedArr.splice(index, 1);
}
toggleCategory(id: number) {
if (this.contains(this.checkedCategory, id)) {
this.remove(this.checkedCategory, id);
} else {
this.add(this.checkedCategory, id);
}
}
I have a (click) event in my checkbox that will call togglecategory
(click)="toggleCategory(category.id)"
Then, when I try to console.log the 'checkedCategory' it's undefined.
I have 3 checkboxes group and I want to reuse the 'contains/add/remove' function that's why I want to pass an array.
Thank you
When you call toggleCategory(20) see what happens, in your case you will see that your function will print add: undefined. so the first thing you must debug is your add function. I think the issue is that your array is not defined. Try to initalize your empty array like this let checkedCategory: Array<number> = Array();
But either way, You need to debug your add function. Good Luck :)
If you have any questions about why this is the solution, let me know, I dont mind sharing the Theory aspect to why this occurs if you are interested.

Javascript - Setting 2 properties to same value - console log shows they have different value

I am looping through an array of documents and setting two properties to the same value, however, doing a console.log shows the two properties have different values.
code:
this.logicItem.$promise.then(() => {
this.logicItem.getDocuments().$promise.then((docs: any) => {
docs.forEach(element => {
if (element.buildProgrammeActivityStatus === BUILD_PROGRAMME_ACTIVITY_STATUS.Confirmed ||
element.buildProgrammeActivityStatus === BUILD_PROGRAMME_ACTIVITY_STATUS.Complete) {
element.upper = true;
element.canUpload = true;
} else {
element.upper = false;
element.canUpload = false;
}
});
console.log(docs);
});
this.logicItem.reload(true);
});
When the code sets both properties to true, console logging the whole array shows that canUpload is always false no matter what, and upper is true if the code entered the true block. What could cause this bizarre behavior? I have tried it using array.map() and some other forms of looping, all with the same result.
The forEach statement should work fine. I tried to call it with some dummy data and I got the expected behavior in all cases. Have you tried to log each element in the forEach statement? If so the problem reproduce? Are the elements as expected?
Also what is the reload function does?
Array.foreach simply runs through an array and does not change the value of array. Array.map runs through the entire array and returns a new array but does not change the array itself you need to manually assign or rewrite the variable.
So here is my suggestion
this.logicItem.$promise.then(() => {
this.logicItem.getDocuments().$promise.then((docs: any) => {
//changed from forEach to map
docs = docs.map( element => {
if (element.buildProgrammeActivityStatus === BUILD_PROGRAMME_ACTIVITY_STATUS.Confirmed ||
element.buildProgrammeActivityStatus === BUILD_PROGRAMME_ACTIVITY_STATUS.Complete) {
element.upper = true;
element.canUpload = true;
} else {
element.upper = false;
element.canUpload = false;
}
//Added a return element
return element.
});
console.log(docs);
});
this.logicItem.reload(true);
});

Angularjs filter always returns true

I've made filter for ng-repaet that looks like this:
$scope.filterRoutine = function(col) {
return _.isEqual(col.Routine.IsIndoor, true);
}
It works fine (isEqual returns true or false).
But this doesn't work, and I don't know why is that (when I say it doesn't work, I don't get any errors, but view doesn't change)
$scope.filterRoutine = function(col) {
return _.forEach(tempData, function (temp) {
if (_.find(col.Exercises, { Exercise: temp })) {
return true;
} else {
return false;
}
});
}
What I do here (or rather what I want to do) is this: I have tempData collection, if my col.Exercises has at least one item from tempData it should be showed in the view.
But for some reason all items are showed in the view i.e. nothing has been filtered.
My guess is that this because this function always returns true (because always at least one col.Exercises should contain item from tempData).
How can I fix this i.e. hide all cols which don't contain any items from tempData ?
Returning from _.forEach does not do what you expect it to do.
You'll need to do something like this:
$scope.filterRoutine = function(col) {
var x = false;
_.forEach(tempData, function (temp) {
if (_.find(col.Exercises, { Exercise: temp })) {
x = true;
}
});
return x
}
Also, "Callbacks may exit iteration early by explicitly returning false.", meaning your return false was stoping iteration after the first time _.find returned false.

Categories

Resources