Do we need to initialize an array? - javascript

Today as I read the PrimeNg's source code, noticed one thing. The author of one utils class (ObjectUtils) in some cases initialized the arrays, but in other ones not.
Example 1 from source code:
public static generateSelectItems(val: any[], field: string): SelectItem[] {
let selectItems: SelectItem[];
if(val && val.length) {
selectItems = [];
for(let item of val) {
selectItems.push({label: this.resolveFieldData(item, field), value: item});
}
}
return selectItems;
}
Example 2 from source code:
public static filter(value: any[], fields: any[], filterValue: string) {
let filteredItems: any[] = [];
let filterText = this.removeAccents(filterValue).toLowerCase();
if(value) {
for(let item of value) {
for(let field of fields) {
let fieldValue = this.removeAccents(String(this.resolveFieldData(item, field))).toLowerCase();
if(fieldValue.indexOf(filterText) > -1) {
filteredItems.push(item);
break;
}
}
}
}
return filteredItems;
}
In second example he initialized the array filteredItems, but in first one does not (selectItems array). I'm wondering why he did so and if there is any best practices regarding this.

This line:
let selectItems: SelectItem[];
declares a variable, but does not create an array. (It doesn't initialize the variable with anything.) Later, the array is created using [], here:
selectItems = [];
// -----------^^
...and then assigned to the variable.
This line:
let filteredItems: any[] = [];
...combines those two steps by providing a variable initializer.
The key thing to understand is that selectItems in the first example doesn't contain anything, at all, until it has something assigned to it. So it isn't that the programmer "hasn't initialized the array," it's that he/she hasn't initialized the variable.
You don't have to initialize a variable when declaring it. Both let and var declarations without an initializer initialize the variable to undefined (at different times, but that's not important here). (You do need to initialize a constant declared with const when declaring it, since you can't assign to it afterward.)
In a comment you asked:
But when this if(val && val.length) fails in first example, then the function will return undefined. Won't be better if it returns let say an empty array?
That depends purely on what the programmer wants to have happen when val is falsy or has a falsy length. I'm sure there are use cases for returning undefined and for returning an empty array.
You may be wondering, though, about returning undefined when the type annotation on the function says it returns SelectItem[]. Remember that by default, null and undefined are effectively members of every type. You can turn that off with the strictNullChecks option, which is documented as:
In strict null checking mode, the null and undefined values are not in the domain of every type and are only assignable to themselves and any (the one exception being that undefined is also assignable to void).
strictNullChecks would indeed have made that function an error. Compare this code:
function foo(bar: boolean) : Date {
let x : Date;
if (bar) {
x = new Date();
}
return x;
}
foo(Math.random() < 0.5);
Try it in the playground using the Options button to enable/disable
strictNullChecks: TypeScript flags an error when it's enabled.

Related

Is there way to create types like Array in typescript

Is there a way to create type in typescript with methods that when I call these methods, they have access to the variable's value? Equals what the array, for example, which has the method find.
Example:
const arrayVar: Array = [1,2,3];
array.find(el => el === 1);
In this case, find has access to the value of the array arrayVar without me having to pass it via a parameter to a function, for example, I wanted to create something in this way, for example:
const myVar: MyCustomType = 5;
myVar.add(2); // Nesse caso, o retorno seria 7.
I know it can be done with classes and functions, but then I would have to pass the value of "myVar" as a parameter (function add (value1, value2), for example), I wanted a way to access it directly, just like the type Array does in its methods.
To make a subclass of Number with new methods:
class SwagNumber extends Number {
add(number: number) {
// Tell the TS compiler that `this` is an unboxed Number
return (this as unknown as number) + number;
}
}
Then to use:
const six = new SwagNumber(6);
six will be typed to SwagNumber by the TS compiler.
And to show it works:
six.add(5)
> 11
Let's look at the Constructor used, part of the Class:
> six.constructor
[class SwagNumber extends Number]
This will also leave the original Number prototype unchanged, which will stop any potential issues (double dots are used to use a method on a number, to distinguish the dot from a decimal point!)
> 3..constructor
[Function: Number]
or:
> (3).constructor
[Function: Number]
See Classes on MDN
However there's some danger here
Since SwagNumber is an object, and a regular number isn't an object by default (until you call its methods), comparisons won't work properly:
> six === 6
false
See Why should you not use Number as a constructor?
You could do this by adding your add method to a prototype:
interface Number {
add: (val:number) => number;
}
Number.prototype.add = function(val:number): number{
return (this as number) + val;
}
var myVal: Number = 5
console.log(myVal.add(7))
TS Playground Link
If you want to invoke functions on an object at this point create a dedicated class. Why? Because you have structured zone where you can add/remove/edit and reuse in other part of code.
Sorry, i write in JS but you can change with no effort in TypeScript
Create Class Element
class CustomArray {
constructor() {
this.arr = [];
}
add(value) {
this.arr.push(value)
}
erase(){
this.arr = [];
}
// so on...
print(){
console.log(this.arr)
}
}
//I use Module, so i will use require.
module.exports = CustomArray;
The above class is simplest example. You can decorate with other functions. In theory as many as you want
FOR USE INSIDE OTHER CONTEXT
const CustomArray = require("./CustomArray");
var arr = new CustomArray();
arr.add(2)
arr.print()

ALGORITHM - File Naming

You are given an array of desired filenames in the order of their
creation. Since two files cannot have equal names, the one which comes
later will have an addition to its name in a form of (k), where k is
the smallest positive integer such that the obtained name is not used
yet.
Return an array of names that will be given to the files.
Example
For names = ["doc", "doc", "image", "doc(1)", "doc"], the output
should be fileNaming(names) = ["doc", "doc(1)", "image", "doc(1)(1)",
"doc(2)"].
One person posted this solution:
const fileNaming = names => {
const used = {};
return names.map(name => {
let newName = name;
while (used[newName]) {
newName = `${name}(${used[name]++})`;
}
used[newName] = 1;
return newName;
});
};
I'm having a hard time understanding the while block's condition.
used is an empty object.
newName is a new variable that is equal to the current item in the names array.
How does used[newName] resolve to a number? used is never set to anything other then an empty object.
This is the console output for console.log(used[newName])
Using this input:
["dd",
"dd(1)",
"dd(2)",
"dd",
"dd(1)",
"dd(1)(2)",
"dd(1)(1)",
"dd",
"dd(1)"]
In JavaScript, {} is an empty object - and as such, it can contain any number of key-value pairs. A key that is not defined has the value undefined, which evaluates to false when tested. However, any non-zero, non-NaN numerical value will evaluate to true:
console.log({}["a key that does not exist"]) // undefined
while (undefined) {
console.log("this should never be printed"); // never executes
}
while ({}["a key that does not exist"]) {
console.log("this should never be printed"); // never executes
}
if (1) {
console.log("positive numbers are true"); // writes to console
}
You can use objects as maps (even though you can also use actual maps instead in newer code)
A simpler version of the above program would have written
return names.map(name => {
let newName = name;
while (used[newName] !== undefined) { // tests for "not undefined"
newName = `${name}(${used[name]})`; // uses current value
used[name] = used[name] + 1; // sets a larger value
}
used[newName] = 1; // sets a value
return newName;
});

Create typescript extension for array of particular class type

I know that i can create a basic Array extension, something like this, which works on all arrays, no matter what the type is within that array.
export {};
declare global {
interface Array<T> {
isEmpty(): boolean;
}
}
// determines if an array is empty, or if the contents of the items in the array are empty
Array.prototype.isEmpty = function() {
if (this.length == 0) {
return true;
} else {
return this.some((c) => c != null && c != '');
}
};
But i would like to create an extension for an array that only contains a particular object? I have tried this, but gives error
import { MyObject } from './my-object';
export {};
declare global {
interface Array<MyObject> {
isEmpty(): boolean;
}
}
// determines if an array is empty, or if the contents of the items in the array are empty
Array.prototype.isEmpty = function() {
if (this.length == 0) {
return true;
} else {
return this.some((c) => c != null && c != '');
}
};
I have tried to find out by looking here but i cannot seem to figure out the correct syntax.
Any help much appreciated :)
What are you trying to do? As #NitzanTomer points out you can't specialize generics like that in TypeScript, where Array<MyObject> has a method isEmpty(), but Array<string> does not. Depending on how you plan to use it, there are things that get you close.
Option 1: Extend Array prototype as you requested (not recommended)
The closest I can get to what you specifically asked for is to require the this parameter of isEmpty() to be of type Array<MyObject>:
declare global {
interface Array<T> {
isEmpty(this: Array<MyObject>): boolean;
}
}
// note the error below
Array.prototype.isEmpty = function() {
if (this.length == 0) {
return true;
} else {
return this.some((c) => c != null &&
c != '' // error! c has to be MyObject, not string
);
}
};
(As an aside, your implementation of isEmpty() has an error when you compare an array element, now required to be MyObject, to a string value. The good news is that TypeScript is alerting you of this so you can fix it.)
You can now go ahead and call isEmpty() on arrays of MyObject:
declare let myObject1: MyObject;
declare let myObject2: MyObject;
const arrayOfMyObject: MyObject[] = [myObject1, myObject2];
arrayOfMyObject.isEmpty(); // okay
Now, the isEmpty() method exists on all arrays, but trying to call it on an array of a different type will give you an error:
const arrayOfStrings: string[] = ['a', 'b'];
arrayOfStrings.isEmpty(); // error, string is not MyObject
So that might work for you.
That being said, extending native prototypes like Array is almost always considered a bad idea.
Option 2: Extend selected Array instances with new method
A better idea would be to create a brand new class with the extra method, or to take individual Array instances and add the method to them without polluting the Array prototype. Here's a way to do the latter:
interface MyObjectArray<T extends MyObject> extends Array<T>{
isEmpty(): boolean;
}
const isEmpty = function(this: Array<MyObject>) {
if (this.length == 0) {
return true;
} else {
return this.some((c) => c != null &&
c != '' // error! c is MyObject, not string
);
}
};
function toMyObjectArray<T extends MyObject>(arr: Array<T>): MyObjectArray<T> {
const ret = arr as MyObjectArray<T>;
ret.isEmpty = isEmpty.bind(ret);
return ret;
}
Now we have a new interface called MyObjectArray which includes the extra method. And if you have an existing instance of MyObject[], you can convert it to MyObjectArray using the toMyObjectArray() function, which adds the isEmpty() method to it. Then you can use it like this:
declare let myObject1: MyObject;
declare let myObject2: MyObject;
const arrayOfMyObject = [myObject1, myObject2];
arrayOfMyObject.isEmpty(); // error, no such method
const myObjectArray = toMyObjectArray(arrayOfMyObject); // convert
myObjectArray.isEmpty(); // okay
// can't convert an array of anything else
const arrayOfStrings = toMyObjectArray(['a', 'b', 'c']);
There's an extra step of calling a conversion function every time you need to use isEmpty(), which might make this less desirable for you.
Option 3: Use standalone isEmpty() function, not method
In fact, if you are not going to mess with Array.prototype, and you have to call a function on the array instance, you might as well just skip the extra interface and use a standalone isEmpty() instead of bothering with methods:
const isMyObjectArrayEmpty = function(arr: Array<MyObject>) {
if (arr.length == 0) {
return true;
} else {
return arr.some((c) => c != null &&
c != '' // error! c is MyObject, not string
);
}
};
declare let myObject1: MyObject;
declare let myObject2: MyObject;
const arrayOfMyObject = [myObject1, myObject2];
isMyObjectArrayEmpty(arrayOfMyObject); // okay
const arrayOfStrings = ['a', 'b', 'c'];
isMyObjectArrayEmpty(arrayOfStrings); // error
Those are the options as I see them.
You cannot extend an array for a particular type using this method. Typescript uses generic type erasure, so at runtime there is no way to distinguish between Array<A> and Array<B>.
You could create a derived array type that adds the required methods, sample code here, but you would have to new up that class instead of creating simple arrays with []

Create custom numeric arrays index in Javascript

I trying a creation of insulted index in Javascript. That´s my code.
var map = [];
function createIndexIfNotExists (posx,posy){
if(typeof(map[posx]===undefined)){
map[posx] = {};
console.log("created: map["+posx+"] typeof="+typeof(map[posx])); //typeof object
}
if(typeof(map[posx][posy]===undefined)){
map[posx][posy] = [];
console.log("created: map["+posx+"]["+posy+"]
typeof="+typeof(map[posx])); //typeof object
}
map[posx][posy].push( {'posx':posx, 'posy':posy }); }
createIndexIfNotExists(10,5);
createIndexIfNotExists(10,6);
But the result is this.
created: map[10] typeof=object
created: map[10][5] typeof=object
created: map[10] typeof=object
created: map[10][6] typeof=object
Why create map[10] two times if typeof is object and not undefined?
On this line you need to move the ()
if(typeof(map[posx]===undefined)){
should be:
if(typeof(map[posx])===undefined){
The same is true for this line:
if(typeof(map[posx][posy])===undefined){
You're finding the type of the comparison which will always evaluate to the string 'boolean' which will evaluate to true.
typeof return the tyoe as a string, so the type checking will be like
if(typeof(map[posx])==="undefined")
and
if(typeof(map[posx][posy])==="undefined")
and also the ( and ) is not required in typeof to wrap the item you are going to check, its a keyword, nit a function. when you are wrapping an expression (map[posx]===undefined in your case) wit () in typeof that meant the preference of executing that expression is higher, and type will be checked upon that result. so the expression resolved map[posx]===undefined resolved first and you are checking the type of the resule true or false

JavaScript TypeError: Cannot read property 'eat' of Undefined

I am working on a project that solves the classic "jumbled word" puzzle found in many newspapers. The basic idea of the project is that it accepts a scrambled word (no spaces or special characters), generates every permutation of the word, checks each permutation against a "dictionary" supplied by my professor, then adds each permutation that is actually an English word to an array that is then processed further to come up with the result.
Currently, I'm running into a problem that arises when I attempt to check if a permutation is in the "dictionary". The code below was provided by my professor and creates a "dictionary" from an external text file. According to him, dictionary[w] should return a number paired with the word that represents the word's frequency or "undefined" if the word is not in the dictionary.
function readDictionary() {
/**
* #type {string[]}
*/
const lines = fs.readFileSync("count_1w.txt", "utf-8").split("\n");
var line;
const dictionary = {};
for (line of lines) {
line = line.trim();
let array = line.split('\t');
if (array.length > 1) {
let word = array[0];
let count = array[1];
if (lexicon[word]) {
dictionary[word] = parseFloat(count);
}
}
}
return Object.freeze(dictionary);
}
function getDictionary() {
if (dictionary === null) {
dictionary = readDictionary();
}
return dictionary;
}
var dictionary = getDictionary();
The following code which I have written should return "true" if dictionary[letters] is not undefined...
function inDict(letters) {
if (dictionary[letters] !== undefined){
return true;
}
else{
return false;
}
}
...however in its current state it throws the TypeError in the title of this post, with 'eat' being the first permutation of the input that is generated. Note that in my actual code readDictionary(), getDictionary(), and var dictionary = getDictionary are all declared above inDict().
If more details are needed please feel free to ask. I've reached the end of my personal knowledge of JavaScript, and multiple Google searches have turned up nothing that has helped in my particular instance. Any suggestions or opinions are greatly appreciated!
The error message is clear: dictionary has the value undefined, so why is that?
The problem is that the function getDictionary returns undefined. The condition dictionary === null is never true because dictionary has the initial value undefined and undefined === null is false.
So what you are really doing is
var dictionary; // initial value is undefined
dictionary = dictionary;
which does nothing.
I don't see why you need getDictionary at all. Just initialize dictionary directly:
var dictionary = readDictionary();
Alternatively you could:
Initialize dictionary with null (but why would you?)
var dictionary = null;
dictionary = getDictionary();
Compare against undefined instead:
function getDictionary() {
if (dictionary === undefined) {
dictionary = readDictionary();
}
return dictionary;
}
Overall, getDictionary is poorly designed because it has an implicit dependency on dictionary but also returns a value.

Categories