How to pass an instance of an array to a function - javascript

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.

Related

Indexed getter in Javascript

I'm using straight Javascript (no JQuery or anything like that, please). I've implemented a class which wraps an array, thus:
class Ctrls
{
_items = new Array();
constructor()
{
this._items = new Array();
}
Add(oCtrl)
{
this._items.push( { key:oCtrl.Name, value:oCtrl } );
}
Clear()
{
this._items = new Array();
}
get Count()
{
return this._items.length;
}
get Item(index)
{
// get the index'th item.
// If item is numeric, this is an index.
// If item is a string, this is a control name
if (Number.isInteger(index))
{
return this._items(index).value;
}
else
{
item = this._items.find(element => (element.value.Name == index));
return item;
}
}
get Items()
{
return this._items; // in case we desperately need to
}
}
I get an error on page load, at get Item(index), which is Uncaught SyntaxError: Getter must not have any formal parameters. I come from C# world and am looking for an equivalent of:
public Ctrl Item(iIndex)
{
get
{
return _items[iIndex];
}
}
How do I index a getter in Javascript?
Edit(1): I've had suggestions to turn get Item into a function, but if I change the definition to this:
function GetItem(index) // returns Ctrl
{
// get the index'th item.
// If item is numeric, this is an index.
// If item is a string, this is a control name
if (Number.isInteger(index))
{
return this._items(index).value;
}
else
{
item = this._items.find(element => (element.value.Name == index));
return item;
}
}
I get this error on pageload: Uncaught SyntaxError: Unexpected identifier at the line function GetItem...
Edit(2): Modified the above to read:
GetItem(index) // returns Ctrl
{
// get the index'th item.
// If item is numeric, this is an index.
// If item is a string, this is a control name
if (Number.isInteger(index))
{
return this._items(index).value;
}
else
{
item = this._items.find(element => (element.value.Name == index));
return item;
}
}
as functions within classes do not use the function keyword, oddly. This now works. Thank all.
"you can't pass parameters to getters in JS". Theoretically: yes, you cannot do that. But practically: functions are first-class citizens in JS, so they can be arguments or return values of a function. You can do it like this:
class GetterWithParameter {
constructor() {
this.array = ["index 0", "index 1", "index 2"]
}
get itemAtIndex() {
return (idx) => this.array[idx]
}
}
const getterWithParameter = new GetterWithParameter()
const idx0 = getterWithParameter.itemAtIndex(0)
const idx1 = getterWithParameter.itemAtIndex(1)
const idx2 = getterWithParameter.itemAtIndex(2)
console.log("item at index 0:", idx0)
console.log("item at index 1:", idx1)
console.log("item at index 2:", idx2)
So, while the getter cannot have arguments, you can return a function that can receive an argument - and use that.
Of course, the usage seems identical to defining a function on the class that requires the same argument - but still, you are using a getter.

Typescript: Function find for array is working but if I check if it's returning a value with an if statement, it doesn't work

I have this code:
class Cart {
private currentCart: item[];
constructor(private pService: ProductService) {
this.currentCart = [];
}
add(product) {
if (this.currentCart.find((i) => i.product === product) != undefined) {
this.currentCart[
this.currentCart.findIndex((i) => i.product === product)
].increaseAmount();
} else {
this.currentCart.push(new item(product));
}
}
}
class Item {
product: any;
amount: number;
constructor(product) {
this.product = product;
this.amount = 1;
}
increaseAmount() {
this.amount++;
}
decreaseAmount() {
this.amount--;
}
}
My problem is the first time I activated the add function, it works, it creates a new item, the second time, if I send a product that is the same one I sent from before, it should not be undefined cause it does exist, but it doesn't enter the if statement, it goes straight to the else and makes a new item, which has the same product.
I think you want to check your product equality by a unique identifier, not the product itself.
The problem with checking objects in JavaScript is:
console.log({} === {}); // false
That's right. An object does not equal an object, unless it is the exact same object. Looking at your code, it seems as though your product object should be the same, since the objects are passed around by reference, but maybe something is going on under the hood of the TypeScript class constructor that causes the objects to not be the same. Or maybe somewhere else in your code is causing them to not be. At any rate, it's best to just check your product by its unique id, like so (simplified code):
add(product) {
if(this.currentCart.find(item => item.product.id === product.id)) {
this.currentCart[this.currentCart.findIndex(item => item.product.id === product.id)].increaseAmount();
} else {
this.currentCart.push(new item(product))
}
}
If you don't have unique IDs on your products, you definitely should consider adding some.

Removing element from array in class method

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

How to use recursion in JavaScript for try do function?

I want to make this code prettier with recursion.
findModel = function(oldModel, ...modelStyles) {
let model = oldModel.elements;
let i = 0;
try {
do {
model = model.children.find(child => child.mStyle === modelStyles[i]);
i += 1;
} while (i < modelStyles.length);
return model;
} catch (e) {
return undefined;
}
};
tried this:
findModel = function(oldModel, ...modelStyles) {
let model = oldModel.elements;
let i = 0;
if (i < modelStyles.length) {
model = model.children.find(child => child.mStyle === modelStyles[i]);
i += 1;
return model;
} else {
return undefined;
}
};
but it's still not working well. in the first code I get only the element, in the second one I get also undefined.
What did I wrong?
As amply noted in comments, you are actually never calling the function recursively.
When it comes to "pretty", I would not go for recursion, but for reduce:
var findModel = function(oldModel, ...modelStyles) {
try {
return modelStyles.reduce((model, style) => model.children.find(child => child.mStyle === style), oldModel.elements);
} catch (e) {} // No need to explicitly return undefined. It is the default
};
If you really need recursion, then first realise that your function expects a first argument type that never occurs again. Only the toplevel model has an elements property, so you can only call this function for ... the top level of your hierarchy.
To make it work, you would need another function that takes the model type as it occurs in the children:
var findModel = function(oldModel, ...modelStyles) {
function recur(model, style, ...modelStyles) {
if (style === undefined) return model;
return recur(model.children.find(child => child.mStyle === style), ...modelStyles);
}
// Need to change the type of the first argument:
try {
return recur(oldModel.elements, ...modelStyles);
} catch (e) {}
};
If you would change the code where the function is called initially, you could of course pass mainmodel.elements instead of mainmodel, so that this type difference problem is resolved. If you can make that change, then the recursive function can become:
var findModel = function(model, style, ...modelStyles) {
if (style === undefined) return model;
try {
return recur(model.children.find(child => child.mStyle === style), ...modelStyles);
} catch (e) {}
};
Still, I would prefer the reduce variant.
The point of recursive function is to call themselves into themselves. In your case, you are calling the function once, but the function never call itself so it just go through once. I'm not sure of the context so I can't fix your code but i can give you an example of recursion.
Lets say we have an object with property. Some are string, some are number and some are objects. If you want to retrieve each key of this object you would need recursion, since you don't know how deep the object goes.
let objectToParse = {
id: 10,
title: 'test',
parent: {
id: 5,
title: 'parent',
someKey: 3,
parent: {
id: 1,
title: 'grand-parent',
parent: null,
someOtherkey: 43
}
}
};
function parseParentKey(object) {
let returnedKey = [];
let ObjectKeys = Object.keys(object);
for(let i = 0; i < ObjectKeys.length; i++) {
if(typeof object[ObjectKeys[i]] === "object" && object[ObjectKeys[i]] !== null) {
// we are calling the methode inside itself because
//the current property is an object.
returnedKey = returnedKey.concat(parseParentKey(object[ObjectKeys[i]]));
}
returnedKey.push(ObjectKeys[i]);
}
return returnedKey;
}
console.log(parseParentKey(objectToParse));
I know this does not answer your question but it gives you a hint on how to use recursion properly. If your first code works, I don't see why you would need to change it in the first place.

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

Categories

Resources