Im encountering an odd problem when going through freeCodeCamp beta.
The "purpose" of this is not modifying the original array and using functional programming techniques to modify arrays.
However I keep getting complaints about the "array" parameter is the remove function not being a valid function:
// the global variable
var bookList = [
"The Hound of the Baskervilles",
"On The Electrodynamics of Moving Bodies",
"PhilosophiƦ Naturalis Principia Mathematica",
"Disquisitiones Arithmeticae"];
/* This function should add a book to the list and return the list */
// New parameters should come before the bookName one
// Add your code below this line
function add (bookListTemp, bookName) {
let newBookArr = bookListTemp;
return newBookArr.push(bookName);
// Add your code above this line
}
/* This function should remove a book from the list and return the list */
// New parameters should come before the bookName one
// Add your code below this line
function remove (bookList,bookName) {
let newArr = bookList.slice();
if (newArr.indexOf(bookName) >= 0) {
return newArr.slice(0, 1, bookName);
// Add your code above this line
}
}
var newBookList = add(bookList, 'A Brief History of Time');
var newerBookList = remove(bookList, 'On The Electrodynamics of Moving Bodies');
var newestBookList = remove(add(bookList, 'A Brief History of Time'),
'On The Electrodynamics of Moving Bodies');
console.log(bookList);
In the remove function i've tried taking the parameter and doing array.slice() method as well as array.concat() method. Since doing let newArr = bookList doesn't actually make a new array correct? it just makes a new copy that references the original array correct?
The exact error I get is TypeError: bookList.slice is not a function
What's even weirder is Array.isArray(bookList) returns true (in the function remove. So I don't understand why it's complaining about array methods?
Your problem is Array.push
return The new length property of the object upon which the method was
called.
You should return array instead
function add (bookListTemp, bookName) {
let newBookArr = bookListTemp;
newBookArr.push(bookName);
// Add your code above this line
return newBookArr;
}
OR
Let's try Array.concat instead
function add (bookListTemp, bookName) {
let newBookArr = bookListTemp;
return newBookArr.concat(bookName);
// Add your code above this line
}
There are two ways to copy the array without mutating it. You will not be able to use the .slice() method on the bookList, because it is an argument in the function and therefore not an array. The work around is var newBookArr = Array.prototype.slice.call(bookListTemp); or [].slice.call(bookListTemp);
This allows you to perform the slice on the bookList when it is an argument. The other way I discovered, when playing around with it - var newBookArr = [].concat(bookListTemp);
When trying var newBookArr = [].push(bookListTemp); we find the original bookList pushed inside the new array. So it is a copy, but as an array within an array. the .concat() method merges the old array into the new.
Related
How would I call my method to my other objects?
Been having lots of trouble with everything I've tried.
I'm not that confident with this stuff, just looking on how to tell if the object is safe to drive or not.
//Create a constructor function called `Track`. It will accept two parameters - the name of the track and the maximum capacity of the track.
let track = function(name, capacity){
this.trackName=name
this.personnel=0;
this.cars=[];
this.cap=capacity;
}
//We'll need a value for the average weight of a person but this value will be the same for all tracks.
//Add a property `personWeight` on the `Track` prototype so that all instances share the same value.
track.prototype.personWeight = 200
//Create three methods on the prototype that will calculate track weight, person weight, and if its safe to drive
function personWeight(){
personnelWeight = this.personWeight * this.personnel
return personnelWeight
}
function trackWeight(){
let carsTotal = function myFunc(total, num) {
return total - num;
}
let weightTotal = (this.personnel * this.personWeight) + (this.carsTotal)
return weightTotal
}
function safeToDrive(){
if(this.trackWeight<this.capacity){
return true
}
}
//Create two track objects
let trackOne = new track ("Daytona", 25000);
trackOne.cars = [1800, 2400, 2700, 3200, 3600, 3800, 4200]
trackOne.personnel = 10
let trackTwo = new track ("Indiana",15000);
trackTwo.cars = [2000, 2300, 2800, 3000, 3500, 3700, 4000]
trackTwo.personnel = 8
//Call the `safeToDrive` method for truck objects.
With the code as it is now, you would use safeToDrive.call(trackOne). However, this is not the straight-forward way you would do it normally.
I guess what you really want is assigning these methods to the prototype:
track.prototype.safeToDrive = function () {
if(this.trackWeight<this.capacity){
return true
}
}
Then you'd call them using trackOne.safeToDrive().
The same goes for personWeight and trackWeight.
A few other observations:
Your check for this.capacity won't work because the property is actually called cap and not capacity according to what you set in your constructor.
safeToDrive currently returns true or nothing, i.e. undefined, and not true or false as you would expect.
You could fix that by either adding an else with return false or simply using return this.trackWeight < this.capacity instead of the whole if condition.
Oh, also, your personnelWeight variable is accidentally made global. Add a let before it. To avoid this in the first place, add 'use strict' at the top of your file to get warned about this issue next time.
I'm not sure what you are doing with carsTotal there though, I guess that should be a member function as well (otherwise you couldn't even call it using this.carsTotal as you do now). Plus, your indention is wrong there. (Put your file through a beautifier to see what I mean.)
Do you mean truck instead of track maybe...?
I built a custom component that filters an array of objects. The filter uses buttons, sets from active to non-active and allows more than one option on/off at the same time.
StackBlitz of my attempt - https://stackblitz.com/edit/timeline-angular-7-ut6fxu
In my demo you will see 3 buttons/options of north, south and east. By clicking on one you make it active and the result should include or exclude a matching "location" either north, south and east.
I have created my methods and structure to do the filtering, I'm struggling with the final piece of logic.
So far I have created a method to create an array of filtered locations depending on what the user clicks from the 3 buttons.
Next this passes to my "filter array" that gets the logic that should compare this filtered array against the original to bring back the array of results that are still remaining.
Its not quite working and not sure why - I originally got this piece of functionality working by using a pipe, but fore reasons do not want to go in that direction.
//the action
toggle(location) {
let indexLocation = this.filteredLocations.indexOf(location);
if (indexLocation >= 0) {
this.filteredLocations = this.filteredLocations.filter(
i => i !== location
);
} else {
this.filteredLocations.push({ location });
}
this.filterTimeLine();
}
// the filter
filterTimeLine() {
this.filteredTimeline = this.timeLine.filter(x =>
this.contactMethodFilter(x)
);
}
//the logic
private contactMethodFilter(entry) {
const myArrayFiltered = this.timeLine.filter(el => {
return this.filteredLocations.some(f => {
return f.location === el.location;
});
});
}
https://stackblitz.com/edit/timeline-angular-7-ut6fxu
Sorry for my expression but u have a disaster in your code. jajaja!. maybe u lost that what u need but the logic in your functions in so wrong. comparing string with objects. filter a array that filter the same array inside... soo u need make a few changes.
One:
this.filteredLocations.push({location});
Your are pushing object. u need push only the string.
this.filteredLocations.push(location);
Two:
filterTimeLine() {
this.filteredTimeline = this.timeLine.filter(x =>
this.contactMethodFilter(x)
);
}
in this function you filter the timeLine array. and inside of contactMethodFilter you call filter method to timeLine again....
See a functional solution:
https://stackblitz.com/edit/timeline-angular-7-rg7k3j
private contactMethodFilter(entry) {
const myArrayFiltered = this.timeLine.filter(el => {
return this.filteredLocations.some(f => {
return f.location === el.location;
});
});
}
This function is not returning any value and is passed to the .filter
Consider returning a boolean based on your logic. Currently the filter gets undefined(falsy) and everything would be filtered out
My aim is to replace the teacher-id(f_teacher) of one outputted array with the teacher name of another array. I wrote a custom filter, that should do the job:
angular.module('core')
.filter('replaceId', function () { //filter, which replaces Id's of one array, with corresponding content of another array
return function (t_D, s_D, t_prop, s_prop) { //data of target, data of source, target property, source property
var replacment = {};
var output = [];
angular.forEach(s_D, function (item) {
replacment[item.id] = item[s_prop]; //replacment - object is filled with 'id' as key and corresponding value
});
angular.forEach(t_D, function (item) {
item[t_prop] = replacment[item[t_prop]]; //ids of target data are replaced with matching value
output.push(item);
});
return output;
}
});
I use a 'ng-repeat' like this:
<tr ng-repeat="class in $ctrl.classes | filter:$ctrl.search | replaceId:$ctrl.teachers:'f_teacher':'prename' | orderBy:sortType:sortReverse">
<td>{{class.level}}</td>
<td>{{class.classNR}}</td>
<td>{{class.f_teacher}}</td>
</tr>
But it only outputs an empty column. Now the strange thing: If I follow the steps with the debugger, it works for the first time the filter is performed. But when it is performed a second time it outputs an empty column.
I noticed that the returned object of the filter overwrites the $ctrl.classes - array, but normally this shouldn't be the case?
Here is a plnkr:
https://plnkr.co/edit/EiW59gbcLI5XmHCS6dIs?p=preview
Why is this happening?
Thank you for your time :)
The first time through your filter the code takes the f_teacher id and replaces it with the teacher name. The second time through it tries to do the same thing except now instead of getting a teachers ID in f_teacher it finds the teacher's name so it doesn't work. You could fix it by making a copy of the classes instead of modifying them directly. e.g.
angular.forEach(t_D, function (item) {
var itemCopy = angular.copy(item);
itemCopy[t_prop] = replacment[itemCopy[t_prop]];
output.push(itemCopy);
});
https://plnkr.co/edit/RDvBGITSAis3da6sWnyi?p=preview
EDIT
Original solution will trigger an infinite digest because the filter returns new instances of objects every time it runs which will cause angular to think something has changed and retrigger a digest. Could you just have a getter function that gets a teachers name instead of using a filter?
$scope.getTeacherName = function(id) {
var matchingTeachers = $scope.teachers.filter(function(teacher) {
return teacher.id == id;
})
//Should always be exactly 1 match.
return matchingTeachers[0].prename;
};
And then in the HTML you could use it like
<tr ng-repeat="class in classes">
<td>{{class.level}}</td>
<td>{{class.classNR}}</td>
<td>{{getTeacherName(class.f_teacher)}}</td>
</tr>
https://plnkr.co/edit/gtu03gQHlRIMsh9vxr1c?p=preview
i am having a problem that i am finding difficult to find information about as i am unaware of the underlying issue.
I am trying to set a property inside an object, and when i console.log the property it gives the anticipated result but when i console.log the entire object, the property inside is different.
defaults:{
uid:undefined,
createSphere:function(uid,coordinates)
{
this.uid = uid;
console.log(this.uid);
console.log(uid);
console.log(this);
console.log(this.uid);
I run the createSphere function in a simple for loop. Here you can see how i assign the uid for the function.
for(i = 0;i<n;i++)
{
coordinates = {x:0,y:5,z:0}
coordinates.x = 0+ (40*Math.sin(Math.PI*(i/2)))
coordinates.z = 0+ (40*Math.cos(Math.PI*(i/2)))
spheres.defaults.createSphere((i+1),coordinates);
}
Here you can see the resulting log when creating the first sphere with the code from the first block. The console.logs are executed directly after eachother, so nothing is able to change the value between the logging. I wanted to upload it in an image for better clarity but i unfortunately cannot.
1
1
Object
action: function (order,impulseVector){
createSphere: function (uid,coordinates)
uid: 2
__proto__: Object
1
So the problem is; when taking the value directly from the property it differs from when using the entire object.
When you call:
spheres.defaults.createSphere((i+1),coordinates);
It isn't creating a new sphere object. Instead it is only updating "spheres.defaults.uid".
Perhaps this is closer to what you are looking for:
var Sphere = function(uid, coordinates){
this.uid = uid;
this.coordinates = coordinates;
}
var spheres = [];
spheres.push(new Sphere(1, null));
spheres.push(new Sphere(2, null));
spheres.push(new Sphere(3, null));
spheres.push(new Sphere(4, null));
console.log(spheres);
I have a function that uses the underscore.js each function to just call a string replace on each item in a list. (Actually a pair of lists):
//Base object
buckets = {
counters: ["stats.REPLACE.msg_delivered",
"stats.REPLACE.delivery_failed"],
timers: ["stats.timers.REPLACE.msg_delivery_timer.median",
"stats.timers.REPLACE.msg_delivery_timer.mean",
"stats.timers.REPLACE.msg_delivery_timer.std",
"stats.timers.REPLACE.msg_delivery_timer.upper"]
};
//function in question
_getNodeTargets = function(node) {
var targets = buckets;
_.each(targets.counters, function(bucket) { bucket = bucket.replace("REPLACE", node);});
_.each(targets.timers, function(bucket) { bucket = bucket.replace("REPLACE", node);});
return targets;
}
I can step into the each function and see that the strings are being replaced and assigned back to bucket. However, when I hit the return the targets object is unchanged with REPLACE still in each of the strings.
I have no doubt I am doing something dumb but for whatever reason I just can't see it.
Thanks in advance!
You have to make use of the other arguments _.each gives your callback:
_.each(targets.counters,
function(bucket, i, target) {
target[i] = bucket.replace("REPLACE", node);
});
The reason for this is that bucket itself is a reference to a string; replacing that reference with one of your own does not also replace the reference stored in the collection (that keeps pointing to the unmodified version). In contrast, target[i] does go and update the collection so the change is visible even after your callback returns.