javascript initialize each value in the object with a default value - javascript

I have the following code to count the number of links in a page:
let temp = {}
for (const page of pages) {
let count = temp[page.outgoingLinks.length];
if (!count) {
count = 0;
}
count++;
temp[page.outgoingLinks.length]=count;
}
Sorry for variable names like temp as I am trying show some code which would help you understand what I am trying to do. Here, I would like to avoid the need for the if check that initializes the value.
In this example, the default value is an integer. But I would like to store even an Array there, something like defaultdict in python. Any ideas on how I can do it in JS?

There is nothing like Python's defaultdict in JavaScript, however you can have a default value when accessing the property by using the nullish-coalescing operator:
let count = temp[page.outgoingLinks.length] ?? 0;

To avoid the need for the if check that initializes the value
for (const page of pages) {
temp[page.outgoingLinks.length] = temp[page.outgoingLinks.length] ? temp[page.outgoingLinks.length]++ : 0;
}
here you can direclty use ternary operator to do so.

As you want multiple default values, you could create an object that contains the default values. This object can be defined in a helper function that takes an argument which specifies the type of default value you want and returns a function that can be called with the value.
If that value is undefined or null, this function returns the required type of default value.
function getInitializer(typeOfDefault) {
const defaults = {
arr: [0, 0, 0],
num: 0,
emptyString: ''
};
return (value) => value ?? defaults[typeOfDefault];
}
// default number
const initialize = getInitializer('num');
const a = initialize(0);
console.log(a);
// default array
const initialize2 = getInitializer('arr');
const b = initialize2(null);
console.log(b);

Related

How typescript implements enumerations?

I was wondering about how typescript would compile an enumeration into a javascript code. So I implemented the following example:
enum Contagens {
UM,
DOIS,
TRES
}
And it was compiled into this:
"use strict";
var Contagens;
(function (Contagens) {
Contagens[Contagens["UM"] = 0] = "UM";
Contagens[Contagens["DOIS"] = 1] = "DOIS";
Contagens[Contagens["TRES"] = 2] = "TRES";
})(Contagens || (Contagens = {}));
But, I don't understand how it works... someone could explain this code to me?
The variable var Contagens; This creates the variable that will hold a reference to the enum.
The argument Contagens || (Contagens = {}) the enum is used if it already exists, and will be set to an empty object, if it doesn't. This allows enums to be extended:
enum Contagens {
UM,
DOIS,
TRES
}
enum Contagens {
CATRE = 4
}
The function function (Contagens) { takes an argument Contagens that is the value from step #2. In this function it will create the entries on the enum object. It has the same name as the outer variable Contagens, so it shadows that variable. But it's the same value, so that doesn't matter much.
The assignment.
Contagens[Contagens["UM"] = 0] = "UM";
The result of an assignment is the value being assigned.* So Contagens["UM"] = 0 does two things. It sets the key "UM" to the value 0, and it returns 0.
That returned 0 is used then in the a second assignment:
Contagens[0] = "UM";
Now the "UM" property has been assigned 0 and the 0 property has been assigned to "UM". The enum now looks like this:
{ UM: 0, "0": "UM" }
This allows you to lookup a value in an enum by its name, or get its name from its value.
Contagens.UM // 0
Contagens[0] // "UM"
Which is handy!
* The result of an assignment when delcaring a variable is undefined, but assigning a property of an object or assigning to an existing variable will return the assigned value. JS is quirky like that.

Typescript / JavaScript compare undefined values

by http method Im downloading dto with some variables and some of them may be undefined.
example Http response (it may contains more variables like SO2, C6H6 etc...):
{
'city_name': 'warszawa',
'pollution_types': {
'CO': 506.0,
'O3': 52.66
}
}
Dtos:
export interface PollutionTypes {
CO: number;
NO2: number;
SO2: number;
PM10: number;
PM25: number;
C6H6: number;
O3: number;
}
export interface AirQualityData {
city_name: string;
pollution_types: PollutionTypes;
}
At this moment Im trying to compare this in this way but it doesnt work.
tmp = pollutionTypes.map(value => value.CO);
if (tmp != undefined || tmp != null) {
//do something
}
When Im trying to display this object on console Im getting something like that:
[undefined]
0: undefined
length: 1
But this is not equal to undefined :/
Do u know how to solve this? Thanks for your answers.
You are getting the correct thing in your console, it shows you that you get an array. The contents just happens to be undefined.
const arrayWithUndefined = [undefined];
console.log(arrayWithUndefined);
You could filter your array and remove undefined values. And then see if your array has values in it.
const types = {
"city_name": "warszawa",
"pollution_types": {
"O3": 52.66
}
}; // NO CO present in this data to simulate undefined.
const pollutionTypes = [types.pollution_types];
tmp = pollutionTypes.map(value => value.CO);
console.log('Before filter:', tmp);
tmp = tmp.filter(function( element ) {
return element !== undefined;
});
console.log('After filter:', tmp);
if (tmp.length) { // Check if tmp has values in it.
console.log('tmp has elements in it');
//do something
} else {
console.log('tmp is empty');
}
This will return tmp is empty because no CO values where in the array.
let us go through the problems proposed in your question, though it might be more useful to add more information such as sample data of your pollutionTypes variable.
Let us begin:
// So currently you have the following mapping code...
tmp = pollutionTypes.map(value => value.CO);
// ^^^^^^
// The problem is that you are mapping an array that does not have the field
// you are referencing in the mapping function. So it will map for sure,
// its just that it will return an array of undefined values of the same
// size as the array
if (tmp != undefined || tmp != null) {
//...
}
// and apparently when you log tmp, you get [undefined]
So here is a proposed explanation and solution:
/*
* The map function is called on arrays, it allows you to convert an array
* of size N to a new array of the same size N by parsing it a mapping function
* that allows you to manipulate and return derived elements of each element in the
* old array.
*/
// [TIP]: so first we should use const when defining tmp
const tmp = pollutionTypes.map(value => value.CO);
// ^^^^^^
// So generally try making sure that your data has the CO field
// before mapping...because you're just gonna get an array of undefined
// fields
// [TIP]: you do not need to specify the undefined logic
if (tmp) {
//...
}
Can you add more information on what pollutionTypes is so a better solution
can come up. Anyway, hope this helps.

Alternate/better ways to initialize JavaScript object that needs multiple static values?

I have a JavaScript object with some static attribute values, dynamic attribute values and methods. Each time I need one of these objects, I will need 10 of them. Each of the 10 objects gets initialized by a dedicated object literal. That happens under 3 different contexts of a user doing something on a data entry form. User actions can cause the contexts to happen in any order, any number of times, but the same 10 objects will always be created in each context. By "same" I mean the static values for a "no_matl" object will be identical each time a "no_matl" object is created ... only a few dynamic attribute values (field value, previous value, date/time, context ID) are different for each context.
Is there a smarter way to do the initialization currently done with the const object literal? Originally I passed a bunch of params to the constructor and initialized the static attributes from those. The object literal approach seemed cleaner. Maybe there's a better way?
// object literals used to initialize a each of the 10
// different type objects.
const FIELD_NOMATERIAL = {
DispName: 'No Material',
DbName: 'NO_MATERIAL',
TrueVal: 'Yes',
InitVal: '',
DispWhenSet: 'yes',
DispWhenNotSet: ''
};
const FIELD_CPCAT = { ... same attributes, different values ...};
const FIELD_HCN = { ... same attributes, different values ...};
// ... 7 more like this ...
// context 1
var no_matl = new MyField(FIELD_NOMATERIAL),
cpcap = new MyField(FIELD_CPCAT),
hcn = new MyField(FIELD_HCN) .... 7 more like this
// object definition
function MyField() {
if (arguments.length == 1 && typeof(arguments[0]) === 'object' ) {
this.DispName = arguments[0].DispName ;
this.DbName = arguments[0].DbName ;
// .... etc for rest of static attributes ...
}
}
Sounds like what you want is a copy of the original object that can change values without changing the original. Try this:
const FIELD_NOMATERIAL = {
DispName: 'No Material',
DbName: 'NO_MATERIAL',
TrueVal: 'Yes',
InitVal: '',
DispWhenSet: 'yes',
DispWhenNotSet: ''
};
function getFreshCopy(original) {
return Object.assign({}, original);
}
var no_matl = getFreshCopy(FIELD_NOMATERIAL);
Using Object.assign({}, obj) will create a new copy that can be changed without the original values changing. no_matl can be adjusted and FIELD_NOMATERIAL remains in its original state.
Note that const means the variable cannot be assigned a new value. It does not mean that the contents of the object cannot be changed. That means the following is true:
const noChange = { a: 7 };
noChange.a = 8; // this is fine because 'a' is allowed to change
noChange = "hello"; // this gives TypeError: Assignment to constant variable.

no-return-assign when mapping over object values

I am trying to create an object whose keys and values are the keys of a different object. However I am getting a linting error:
ESLint: Arrow function should not return assignment.(no-return-assign)
const obj = {
a: 1, b: 2, c: 3,
};
const options: {[key: string]: string} = {};
Object.keys(obj).map(i => options[i] = i);
JS 101.
When you use an arrow function without brackets, you should always put a returned value on the right side. If you want to run options[i] = i, you should put brackets around it and use forEach instead of map. map returns another array that contains all the returned values from the provided function inside.
Fix it as follows.
Object.keys(obj).forEach((i) => {
options[i] = i;
});
However, since you say you want to create an object with keys and values from the values and keys of another object, you may use the following codes.
options = Object.keys(obj).reduce((prev, current) => {
return {
...prev,
current: obj[current],
};
}, {});

Cannot update local variable

Have been trying to update a local variable (which is an array) via add/remove functions. I was able to add items using add function which also updated my local array but when I tried to remove the same using my code, it still returns my old array without modifications.
However, if I try to use pop() for removing an element, everything seems to work fine.
I know that filter from my remove function is returning the modified array but it's not getting updated in my array named mainArr.
Why is the array getting updated when I replace remove functionality with mainArr.pop() but not with my code.
The code also seems to work if I replace the assignment operator from my remove function to this.mainArr = //return value from the filter.
My remove function seems to be creating a new local variable with the same name mainArr due to which it is not updating my actual mainArr. Why is it doing so? Isn't it a concept of closures that my inner function can access global variables? What am I missing here?
function test() {
let mainArr = [];
function add(func) {
mainArr.push(func);
}
function remove(num) {
mainArr = mainArr.filter(item => item !== num)
}
return {
mainArr,
add,
remove
}
}
let val = test()
val.mainArr // returns []
val.add(3)
val.add(5)
val.mainArr //returns [3, 5]
val.remove(3)
console.log(val.mainArr) // still returns [3, 5]. Why?
mainArr.push(func); mutates the array.
mainArr.filter(item => item !== num) creates a new array.
let mainArr = []; is a variable which holds your original array. Later on, you assign the filtered version to the variable.
return { mainArr, add, remove } returns the value of mainArr which (at the time) is the original array. When you later change the value of the mainArr variable, the previously returned value is still the original array (no time travel is performed!).
Create the object upfront, and then always modify properties of that object. Don't create it from variables which later have their values changed.
function test() {
function add(func) {
data.mainArr.push(func);
}
function remove(num) {
data.mainArr = data.mainArr.filter(item => item !== num)
}
const data = {
mainArr: [],
add,
remove
};
return data;
}
let val = test()
console.log(val.mainArr);
val.add(3)
val.add(5)
console.log(val.mainArr)
val.remove(3)
console.log(val.mainArr)
In modern JS, this sort of thing is generally done with a class rather than a factory though.
class Test {
constructor() {
this.mainArr = [];
}
add(value) {
this.mainArr.push(value);
}
remove(value) {
this.mainArr = this.mainArr.filter(item => item !== value)
}
}
let val = new Test()
console.log(val.mainArr);
val.add(3)
val.add(5)
console.log(val.mainArr)
val.remove(3)
console.log(val.mainArr)

Categories

Resources