diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index a2bb539..f48d2a6 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -6,6 +6,7 @@ import { HomeComponent } from './layout/home/home.component'; import { PageNotFoundComponent } from './layout/page-not-found/page-not-found.component'; import { DsaMainComponent } from './layout/dsa-main/dsa-main.component'; import { InterviewQuestionComponent } from './layout/interview-question/interview-question.component'; +import { ProblemSolvingComponent } from './layout/problem-solving/problem-solving.component'; const routes: Routes = [ { @@ -64,6 +65,18 @@ const routes: Routes = [ } ] }, + { + path:'problem-solving-trick', + component: ProblemSolvingComponent, + children:[ + { + path:'', + loadChildren: () => import('./components/problem-solving/problem-solving.module').then( + (m) => m.ProblemSolvingModule + ) + }, + ] + }, { path: '**', component: PageNotFoundComponent, diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 40cefff..3cff36b 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -15,6 +15,7 @@ import { ServiceWorkerModule } from '@angular/service-worker'; import { environment } from '../environments/environment'; import { DsaMainComponent } from './layout/dsa-main/dsa-main.component'; import { InterviewQuestionComponent } from './layout/interview-question/interview-question.component'; +import { ProblemSolvingComponent } from './layout/problem-solving/problem-solving.component'; @NgModule({ @@ -28,6 +29,7 @@ import { InterviewQuestionComponent } from './layout/interview-question/intervie PageNotFoundComponent, DsaMainComponent, InterviewQuestionComponent, + ProblemSolvingComponent, ], imports: [ FormsModule, diff --git a/src/app/components/problem-solving/problem-solving-routing.module.ts b/src/app/components/problem-solving/problem-solving-routing.module.ts new file mode 100644 index 0000000..7d85075 --- /dev/null +++ b/src/app/components/problem-solving/problem-solving-routing.module.ts @@ -0,0 +1,18 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; +import { StringProblemComponent } from './string-problem/string-problem.component'; + +const routes: Routes = [ + { + path:'', pathMatch:'full', redirectTo:'string' + }, + { + path:'string', component: StringProblemComponent + } +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] +}) +export class ProblemSolvingRoutingModule { } diff --git a/src/app/components/problem-solving/problem-solving.module.ts b/src/app/components/problem-solving/problem-solving.module.ts new file mode 100644 index 0000000..f027f95 --- /dev/null +++ b/src/app/components/problem-solving/problem-solving.module.ts @@ -0,0 +1,17 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +import { ProblemSolvingRoutingModule } from './problem-solving-routing.module'; +import { StringProblemComponent } from './string-problem/string-problem.component'; + + +@NgModule({ + declarations: [ + StringProblemComponent + ], + imports: [ + CommonModule, + ProblemSolvingRoutingModule + ] +}) +export class ProblemSolvingModule { } diff --git a/src/app/components/problem-solving/string-problem/string-problem.component.css b/src/app/components/problem-solving/string-problem/string-problem.component.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/components/problem-solving/string-problem/string-problem.component.html b/src/app/components/problem-solving/string-problem/string-problem.component.html new file mode 100644 index 0000000..9a0f0b2 --- /dev/null +++ b/src/app/components/problem-solving/string-problem/string-problem.component.html @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/src/app/components/problem-solving/string-problem/string-problem.component.spec.ts b/src/app/components/problem-solving/string-problem/string-problem.component.spec.ts new file mode 100644 index 0000000..778669d --- /dev/null +++ b/src/app/components/problem-solving/string-problem/string-problem.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { StringProblemComponent } from './string-problem.component'; + +describe('StringProblemComponent', () => { + let component: StringProblemComponent; + let fixture: ComponentFixtureJavaScript is a loosely typed or a dynamic language because variables in JavaScript are not directly associated with any particular value type, and any variable can be assigned/reassigned with values of all types.
+
+ let age = 50; // age is a number now
+ age = "old"; // age is a string now
+ age = true; // age is a boolean
+
+ `
+ },
+ {
+ slug: "why-is-javascript-treated-as-single-threaded",
+ question: "Why is JavaScript treated as Single threaded",
+ answer: `
+ JavaScript is a single-threaded language. This is because the language specification does not allow the programmer to write code so that the interpreter can run parts of it in parallel in multiple threads or processes. Whereas languages like Java, Go, and C++ can make multi-threaded and multi-process programs.
+ ` + }, + { slug: "how-javascript-runs-in-the-browser", question: "How javascript runs in the browser", answer: `Whenever the call stack receives an async function, it is moved into the Web API. Based on the function, Web API executes it and awaits the result. Once it is finished, it moves the callback into the event queue (the callback of the promise is moved into the microtask queue).
The event loop constantly checks whether or not the call stack is empty. Once the call stack is empty and there is a callback in the event queue, the event loop moves the callback into the call stack. But if there is a callback in the microtask queue as well, it is moved first. The microtask queue has a higher priority than the event queue.
` + }, + { + slug: "what-is-heap", + question: "What is heap", + answer: `Heap (or memory heap) is the memory location where objects are stored when we define variables. This is the place where all memory allocations and de-allocations take place. Both heap and call-stack are two containers of JS runtime. Whenever runtime comes across variables and function declarations in the code, it stores them in the Heap.
+ + ` + }, + { + slug: "what-is-an-event-table", + question: "What is an event table", + answer: `Event Table is a data structure that stores and keeps track of all the events which will be executed asynchronously like after some time interval or after the resolution of some API requests. Whenever you call a setTimeout
function or invoke async operation, it is added to the Event Table. It doesn't execute functions on its own. The main purpose of the event table is to keep track of events and send them to the Event Queue as shown in the below diagram.
Scope is the accessibility of variables, functions, and objects in some particular part of your code during runtime. In other words, scope determines the visibility of variables and other resources in areas of your code.
+ ` + },{ + slug: "what-is-the-purpose-of-the-let-keyword", + question: "What is the purpose of the let keyword", + answer: `The let
statement declares a block scope local variable. Hence the variables defined with let keyword are limited in scope to the block, statement, or expression on which it is used. Whereas variables declared with the var
keyword used to define a variable globally, or locally to an entire function regardless of block scope.
Let's take an example to demonstrate the usage,
+
+ let counter = 30;
+ if (counter === 30) {
+ let counter = 31;
+ console.log(counter); // 31
+ }
+ console.log(counter); // 30 (because the variable in if block won't exist here)
+
+ `
+ },{
+ slug: "what-is-the-difference-between-let-and-var",
+ question: "What is the difference between let and var",
+ answer: `You can list out the differences in a tabular format
+var | +let | +
---|---|
It has been available from the beginning of JavaScript | +Introduced as part of ES6 | +
It has function scope | +It has block scope | +
Variables will be hoisted | +Hoisted but not initialized | +
Let's take an example to see the difference,
+
+ function userDetails(username) {
+ if (username) {
+ console.log(salary); // undefined due to hoisting
+ console.log(age); // ReferenceError: Cannot access 'age' before initialization
+ let age = 30;
+ var salary = 10000;
+ }
+ console.log(salary); //10000 (accessible due to function scope)
+ console.log(age); //error: age is not defined(due to block scope)
+ }
+ userDetails("John");
+
+ `
+ },
+ {
+ slug: "what-are-global-variables",
+ question: "What are global variables",
+ answer: `Global variables are those that are available throughout the length of the code without any scope. The var
keyword is used to declare a local variable but if you omit it then it will become a global variable.
+ msg = "Hello"; // var is missing, it becomes global variable
+
+ `
+ },{
+ slug: "what-are-the-problems-with-global-variables",
+ question: "What are the problems with global variables",
+ answer: `The problem with global variables is the conflict of variable names of local and global scope. It is also difficult to debug and test the code that relies on global variables.
+ ` + },{ + slug: "what-is-the-temporal-dead-zone", + question: "What is the Temporal Dead Zone", + answer: `The Temporal Dead Zone is a behavior in JavaScript that occurs when declaring a variable with the let
and const
keywords, but not with var
. In ECMAScript 6, accessing a let
or const
variable before its declaration (within its scope) causes a ReferenceError
. The time span when that happens, between the creation of a variable’s binding and its declaration, is called the temporal dead zone.
Let's see this behavior with an example,
+
+ function somemethod() {
+ console.log(counter1); // undefined
+ console.log(counter2); // ReferenceError
+ var counter1 = 1;
+ let counter2 = 2;
+ }
+
+ `
+ },{
+ slug: "what-is-hoisting",
+ question: "What is Hoisting",
+ answer: `Hoisting is a JavaScript mechanism where variables, function declarations, and classes are moved to the top of their scope before code execution. Remember that JavaScript only hoists declarations, not initialization.
+Let's take a simple example of variable hoisting,
+
+ console.log(message); //output : undefined
+ var message = "The variable Has been hoisted";
+
+ The above code looks like as below to the interpreter,
+
+ var message;
+ console.log(message);
+ message = "The variable Has been hoisted";
+
+ In the same fashion, function declarations are hoisted too
+
+ message("Good morning"); //Good morning
+
+ function message(name) {
+ console.log(name);
+ }
+
+ This hoisting makes functions to be safely used in code before they are declared.
+ ` + },{ + slug: "how-do-you-redeclare-variables-in-a-switch-block-without-an-error", + question: "How do you redeclare variables in a switch block without an error", + answer: `If you try to redeclare variables in a switch block
then it will cause errors because there is only one block. For example, the below code block throws a syntax error as below,
+ let counter = 1;
+ switch (x) {
+ case 0:
+ let name;
+ break;
+
+ case 1:
+ let name; // SyntaxError for redeclaration.
+ break;
+ }
+
+ To avoid this error, you can create a nested block inside a case clause and create a new block scoped lexical environment.
+
+ let counter = 1;
+ switch (x) {
+ case 0: {
+ let name;
+ break;
+ }
+ case 1: {
+ let name; // No SyntaxError for redeclaration.
+ break;
+ }
+ }
+
+ `
+ },{
+ slug: "is-that-possible-to-use-expressions-in-switch-cases",
+ question: "Is that possible to use expressions in switch cases?",
+ answer: `
+ You might have seen expressions used in switch condition but it is also possible to use for switch cases by assigning true value for the switch condition. Let's see the weather condition based on temperature as an example:
+ +
+ const weather = (function getWeather(temp) {
+ switch (true) {
+ case temp < 0:
+ return "freezing";
+ case temp < 10:
+ return "cold";
+ case temp < 24:
+ return "cool";
+ default:
+ return "unknown";
+ }
+ })(10);
+
+ `
+ },
+
+ {
+ slug: "what-is-the-output-of-below-for-loops",
+ question: "What is the output of below for loops",
+ answer: `
+ for (var i = 0; i < 4; i++) {
+ // global scope
+ setTimeout(() => console.log(i));
+ }
+
+ for (let i = 0; i < 4; i++) {
+ // block scope
+ setTimeout(() => console.log(i));
+ }
+
+ The output of the above for loops is 4 4 4 4
and 0 1 2 3
Explanation: Due to the event queue/loop of JavaScript, the setTimeout
callback function is called after the loop has been executed. Since the variable i
is declared with the var
keyword, it became a global variable and the value was equal to 4 using iteration when the time setTimeout
function is invoked. Hence, the output of the first loop is 4 4 4 4
.
Whereas in the second loop, the variable i
is declared as the let
keyword, it becomes a block scoped variable and it holds a new value (0, 1, 2, 3) for each iteration. Hence, the output of the second loop is 0 1 2 3
.
No, you cannot redeclare let and const variables. If you do, it throws below error
+Uncaught SyntaxError: Identifier 'someVariable' has already been declared
+
+ Explanation: The variable declaration with var
keyword refers to a function scope and the variable is treated as if it were declared at the top of the enclosing scope due to the hoisting feature. So all the multiple declarations contributing to the same hoisted variable without any error. Let's take an example of re-declaring variables in the same scope for both var
and let/const
variables.
+ var name = "John";
+ function myFunc() {
+ var name = "Nick";
+ var name = "Abraham"; // Re-assigned in the same function block
+ alert(name); // Abraham
+ }
+ myFunc();
+ alert(name); // John
+
+ The block-scoped multi-declaration throws a syntax error,
+
+ let name = "John";
+ function myFunc() {
+ let name = "Nick";
+ let name = "Abraham"; // Uncaught SyntaxError: Identifier 'name' has already been declared
+ alert(name);
+ }
+
+ myFunc();
+ alert(name);
+
+ `
+ },{
+ slug: "what-is-the-output-of-below-function-calls",
+ question: "What is the output of below function calls",
+ answer: `Code snippet:
+
+ const circle = {
+ radius: 20,
+ diameter() {
+ return this.radius * 2;
+ },
+ perimeter: () => 2 * Math.PI * this.radius,
+ };
+
+
+ console.log(circle.diameter());
+ console.log(circle.perimeter());
+
+ Output:
+The output is 40
and NaN
. Remember that diameter
is a regular function, whereas the value of perimeter
is an arrow function. The this
keyword of a regular function (i.e., diameter
) refers to the surrounding scope, which is a circle
object. Whereas the this
keyword of perimeter
function refers to the surrounding scope, which is a window
object. Since there is no radius
property on window
objects, it returns an undefined
value, and the multiplication of undefined
and Math.PI
results in NaN
(Not a Number) value.
Below are the major differences between undeclared (not defined) and undefined variables,
+undeclared | +undefined | +
---|---|
These variables do not exist in a program and are not declared | +These variables declared in the program but have not assigned any value | +
If you try to read the value of an undeclared variable, then a runtime error is encountered | +If you try to read the value of an undefined variable, an undefined value is returned. | +
Below are the main differences between null and undefined:
+Null | +Undefined | +
---|---|
It is an assignment value which indicates that variable points to no object. | +It is not an assignment value where a variable has been declared but has not yet been assigned a value. | +
Type of null is object | +Type of undefined is undefined | +
The null value is a primitive value that represents the null, empty, or non-existent reference. | +The undefined value is a primitive value used when a variable has not been assigned a value. | +
Indicates the absence of a value for a variable | +Indicates absence of variable itself | +
Converted to zero (0) while performing primitive operations | +Converted to NaN while performing primitive operations | +
A primitive data type is data that has a primitive value (which has no properties or methods). There are 7 types of primitive data types:
+Primitive Values like string, number, and boolean don't have properties and methods, but they are temporarily converted or coerced to an object (Wrapper object) when you try to perform actions on them. For example, if you apply toUpperCase()
method on a primitive string value, it does not throw an error but returns uppercase of the string.
+ let name = "john";
+
+ console.log(name.toUpperCase()); // Behind the scenes treated as console.log(new String(name).toUpperCase());
+
+ i.e., Every primitive except null and undefined have Wrapper Objects and the list of wrapper objects are String, Number, Boolean, Symbol, and BigInt.
+ ` + },{ + slug: "what-is-the-difference-between-isnan-and-number-isnan", + question: "What is the difference between isNaN and Number.isNaN?", + answer: ` +isNaN
converts the argument to a Number and returns true if the resulting value is NaN.Let's see the difference with an example:
+ +
+ isNaN('hello'); // true
+ Number.isNaN('hello'); // false
+
+ `
+ }
+ ,{
+ slug: "how-to-detect-primitive-or-non-primitive-value-type",
+ question: "How do you detect primitive or non-primitive value type",
+ answer: `
+ In JavaScript, primitive types include boolean, string, number, BigInt, null, Symbol, and undefined. Whereas non-primitive types include the Objects. But you can easily identify them with the below function:
+
+ var myPrimitive = 30;
+ var myNonPrimitive = {};
+ function isPrimitive(val) {
+ return Object(val) !== val;
+ }
+
+ isPrimitive(myPrimitive);
+ isPrimitive(myNonPrimitive);
+
+ If the value is a primitive data type, the Object constructor creates a new wrapper object for the value. But If the value is a non-primitive data type (an object), the Object constructor will give the same object.
+ ` + },{ + slug: "what-is-pass-by-value-and-pass-by-reference", + question: "What is pass by value and pass by reference?", + answer: ` +Pass-by-value creates a new space in memory and makes a copy of a value. Primitives such as string, number, boolean etc will actually create a new copy. Hence, updating one value doesn't impact the other value. i.e., The values are independent of each other.
+
+ let a = 5;
+ let b = a;
+
+ b++;
+ console.log(a, b); // 5, 6
+
+ In the above code snippet, the value of a
is assigned to b
and the variable b
has been incremented. Since there is a new space created for variable b
, any update on this variable doesn't impact the variable a
.
Pass by reference doesn't create a new space in memory but the new variable adopts a memory address of an initial variable. Non-primitives such as objects, arrays, and functions get the reference of the initial variable. i.e., updating one value will impact the other variable.
+
+ let user1 = {
+ name: "John",
+ age: 27,
+ };
+ let user2 = user1;
+ user2.age = 30;
+
+ console.log(user1.age, user2.age); // 30, 30
+
+ In the above code snippet, updating the age
property of one object will impact the other property due to the same reference.
JavaScript language has both primitives and non-primitives but there are few differences between them as below:
+Primitives | +Non-primitives | +
---|---|
These types are predefined | +Created by developer | +
These are immutable | +Mutable | +
Compare by value | +Compare by reference | +
Stored in Stack | +Stored in heap | +
Contain certain value | +Can contain NULL too | +
You can use the logical OR operator ||
in an assignment expression to provide a default value. The syntax looks like as below,
+ var a = b || c;
+
+ As per the above expression, variable 'a' will get the value of 'c' only if 'b' is falsy (if it is null, false, undefined, 0, empty string, or NaN), otherwise 'a' will get the value of 'b'.
+ ` + },{ + slug: "what-are-the-benefits-of-keeping-declarations-at-the-top", + question: "What are the benefits of keeping declarations at the top", + answer: `It is recommended to keep all declarations at the top of each script or function. The benefits of doing this are,
+It is recommended to initialize variables because of the below benefits,
+A local variable takes precedence over a global variable with the same name. Let's see this behavior in an example.
+
+ var msg = "Good morning";
+ function greeting() {
+ msg = "Good Evening";
+ console.log(msg); // Good Evening
+ }
+ greeting();
+
+ `
+ },{
+ slug: "what-are-various-operators-supported-by-javascript",
+ question: "What are various operators supported by JavaScript",
+ answer: `An operator is capable of manipulating (mathematical and logical computations) a certain value or operand. There are various operators supported by JavaScript as below,
+typeof variable
Below are the list of bitwise logical operators used in JavaScript
+Spread operator allows iterables (arrays / objects / strings) to be expanded into single arguments/elements. Let's take an example to see this behavior,
+
+ function calculateSum(x, y, z) {
+ return x + y + z;
+ }
+
+ const numbers = [1, 2, 3];
+
+ console.log(calculateSum(...numbers)); // 6
+
+ `
+ },{
+ slug: "what-is-an-unary-operator",
+ question: "What is an Unary operator",
+ answer: `The unary (+) operator is used to convert a variable to a number. If the variable cannot be converted, it will still become a number but with the value NaN. Let's see this behavior in action.
+
+ var x = "100";
+ var y = +x;
+ console.log(typeof x, typeof y); // string, number
+
+ var a = "Hello";
+ var b = +a;
+ console.log(typeof a, typeof b, b); // string, number, NaN
+
+ `
+ },{
+ slug: "what-is-a-comma-operator",
+ question: "What is a comma operator",
+ answer: `The comma operator is used to evaluate each of its operands from left to right and returns the value of the last operand. This is totally different from comma usage within arrays, objects, and function arguments and parameters. For example, the usage for numeric expressions would be as below,
+
+ var x = 1;
+ x = (x++, x);
+
+ console.log(x); // 2
+
+ `,
+ },
+ {
+ slug: "what-is-the-advantage-of-a-comma-operator",
+ question: "What is the advantage of a comma operator",
+ answer: `It is normally used to include multiple expressions in a location that requires a single expression. One of the common usages of this comma operator is to supply multiple parameters in a for
loop. For example, the below for
loop uses multiple expressions in a single location using comma operator,
+ for (var a = 0, b =10; a <= 10; a++, b--)
+
+ You can also use the comma operator in a return statement where it processes before returning.
+
+ function myFunction() {
+ var a = 1;
+ return (a += 10), a; // 11
+ }
+
+ `
+ },{
+ slug: "what-is-a-void-operator",
+ question: "What is a void operator",
+ answer: `The void
operator evaluates the given expression and then returns undefined
(i.e., without returning value). The syntax would be as below,
+ void expression;
+ void expression;
+
+ Let's display a message without any redirection or reload
+
+ <a href="javascript:void(alert('Welcome to JS world'))">
+ Click here to see a message
+ </a>
+
+ Note: This operator is often used to obtain the undefined
primitive value, using "void(0)
".
+ [..."John Resig"];
+
+ The output of the array is ['J', 'o', 'h', 'n', ' ', 'R', 'e', 's', 'i', 'g']
Explanation: The string is an iterable type and the spread operator within an array maps every character of an iterable to one element. Hence, each character of a string becomes an element within an Array.
+ ` + },{ + slug: "what-is-the-purpose-of-double-tilde-operator", + question: "What is the purpose of double tilde operator", + answer: `The double tilde operator (~~
) is known as double NOT bitwise operator. This operator is a slightly quicker substitute for Math.floor()
.
If you prepend the additive (+) operator on falsy values (null, undefined, NaN, false, ""), the falsy value converts to a number value zero. Let's display them on the browser console as below,
+
+ console.log(+null); // 0
+ console.log(+undefined); // NaN
+ console.log(+false); // 0
+ console.log(+NaN); // NaN
+ console.log(+""); // 0
+
+ `
+ },{
+ slug: "what-is-nullish-coalescing-operator",
+ question: "What is nullish coalescing operator (??)",
+ answer: `It is a logical operator that returns its right-hand side operand when its left-hand side operand is null or undefined, and otherwise returns its left-hand side operand. This can be contrasted with the logical OR (||
) operator, which returns the right-hand side operand if the left operand is any falsy value, not only null or undefined.
+ console.log(null ?? true); // true
+ console.log(false ?? true); // false
+ console.log(undefined ?? true); // true
+
+ `
+ },{
+ slug: "what-is-the-difference-between-and-operators",
+ question: "What is the difference between == and === operators",
+ answer: `JavaScript provides both strict (===
, !==
) and type-converting (==
, !=
) equality comparison. The strict operators take the type of variable into consideration, while non-strict operators make type correction/conversion based upon values of variables. The strict operators follow the below conditions for different types,
===
, but equal with ==
, i.e, null===undefined
--> false, but null==undefined
--> trueSome of the examples which cover the above cases:
+
+ 0 == false // true
+ 0 === false // false
+ 1 == "1" // true
+ 1 === "1" // false
+ null == undefined // true
+ null === undefined // false
+ '0' == false // true
+ '0' === false // false
+ []==[] or []===[] //false, refer different objects in memory
+ {}=={} or {}==={} //false, refer different objects in memory
+
+ `
+ },{
+ slug: "what-are-lambda-or-arrow-functions",
+ question: "What are lambda or arrow functions",
+ answer: `An arrow function is a shorter syntax for a function expression and does not have its own this, arguments, super, or new.target. These functions are best suited for non-method functions, and they cannot be used as constructors.
+ ` + },{ + slug: "what-is-a-first-class-function", + question: "What is a first class function", + answer: `In JavaScript, functions are first-class objects. First-class functions mean when functions in that language are treated like any other variable.
+For example, in such a language, a function can be passed as an argument to other functions, can be returned by another function, and can be assigned as a value to a variable. For example, in the below example, handler functions assigned to a listener
+
+ const handler = () => console.log("This is a click handler function");
+ document.addEventListener("click", handler);
+
+ `
+ },
+ {
+ slug: "what-is-a-first-order-function",
+ question: "What is a first order function",
+ answer: `A first-order function is a function that doesn’t accept another function as an argument and doesn’t return a function as its return value.
+
+ const firstOrder = () => console.log("I am a first order function!");
+
+ `
+ },
+ {
+ slug: "what-is-a-higher-order-function",
+ question: "What is a higher order function",
+ answer: `A higher-order function is a function that accepts another function as an argument or returns a function as a return value or both.
+
+ const firstOrderFunc = () =>
+ console.log("Hello, I am a First order function");
+ const higherOrder = (ReturnFirstOrderFunc) => ReturnFirstOrderFunc();
+ higherOrder(firstOrderFunc);
+
+ `
+ },
+ {
+ slug: "what-is-a-unary-function",
+ question: "What is a unary function",
+ answer: `A unary function (i.e., monadic) is a function that accepts exactly one argument. It stands for a single argument accepted by a function.
+Let us take an example of a unary function,
+
+ const unaryFunction = (a) => console.log(a + 10); // Add 10 to the given argument and display the value
+
+ `
+ },
+ {
+ slug: "what-is-the-currying-function",
+ question: "What is the currying function",
+ answer: `Currying is the process of taking a function with multiple arguments and turning it into a sequence of functions each with only a single argument. Currying is named after a mathematician Haskell Curry. By applying currying, an n-ary function turns into a unary function.
+Let's take an example of an n-ary function and how it turns into a currying function,
+
+ const multiArgFunction = (a, b, c) => a + b + c;
+ console.log(multiArgFunction(1, 2, 3)); // 6
+
+ const curryUnaryFunction = (a) => (b) => (c) => a + b + c;
+ curryUnaryFunction(1); // returns a function: b => c => 1 + b + c
+ curryUnaryFunction(1)(2); // returns a function: c => 3 + c
+ curryUnaryFunction(1)(2)(3); // returns the number 6
+
+ Curried functions are great to improve code reusability and functional composition.
+ ` + }, + { + slug: "what-is-a-pure-function", + question: "What is a pure function", + answer: `A Pure function is a function where the return value is only determined by its arguments without any side effects. i.e., If you call a function with the same arguments 'n' number of times and 'n' number of places in the application then it will always return the same value.
+Let's take an example to see the difference between pure and impure functions,
+
+ //Impure
+ let numberArray = [];
+ const impureAddNumber = (number) => numberArray.push(number);
+ //Pure
+ const pureAddNumber = (number) => (argNumberArray) =>
+ argNumberArray.concat([number]);
+
+ //Display the results
+ console.log(impureAddNumber(6)); // returns 1
+ console.log(numberArray); // returns [6]
+ console.log(pureAddNumber(7)(numberArray)); // returns [6, 7]
+ console.log(numberArray); // returns [6]
+
+ As per the above code snippets, the Push function is impure itself by altering the array and returning a push number index independent of the parameter value, whereas Concat on the other hand takes the array and concatenates it with the other array producing a whole new array without side effects. Also, the return value is a concatenation of the previous array.
+Remember that Pure functions are important as they simplify unit testing without any side effects and no need for dependency injection. They also avoid tight coupling and make it harder to break your application by not having any side effects. These principles are coming together with the Immutability concept of ES6: giving preference to const over let usage.
+ ` + },{ + slug: "what-is-an-iife-immediately-invoked-function-expression", + question: "What is an IIFE (Immediately Invoked Function Expression)", + answer: `IIFE (Immediately Invoked Function Expression) is a JavaScript function that runs as soon as it is defined. The signature of it would be as below,
+
+ (function () {
+ // logic here
+ })();
+
+ The primary reason to use an IIFE is to obtain data privacy because any variables declared within the IIFE cannot be accessed by the outside world. i.e, If you try to access variables from the IIFE then it throws an error as below,
+
+ (function () {
+ var message = "IIFE";
+ console.log(message);
+ })();
+ console.log(message); //Error: message is not defined
+
+ `
+ },{
+ slug: "what-are-closures",
+ question: "What are closures",
+ answer: `A closure is the combination of a function and the lexical environment within which that function was declared. i.e, It is an inner function that has access to the outer or enclosing function’s variables. The closure has three scope chains
+Let's take an example of closure concept,
+
+ function Welcome(name) {
+ var greetingInfo = function (message) {
+ console.log(message + " " + name);
+ };
+ return greetingInfo;
+ }
+ var myFunction = Welcome("John");
+ myFunction("Welcome "); //Output: Welcome John
+ myFunction("Hello Mr."); //output: Hello Mr.John
+
+ As per the above code, the inner function(i.e, greetingInfo) has access to the variables in the outer function scope(i.e, Welcome) even after the outer function has returned.
+ ` + },{ + slug: "what-is-a-callback-function", + question: "What is a callback function", + answer: `A callback function is a function passed into another function as an argument. This function is invoked inside the outer function to complete an action. + Let's take a simple example of how to use callback function
+
+ function callbackFunction(name) {
+ console.log("Hello " + name);
+ }
+
+ function outerFunction(callback) {
+ let name = prompt("Please enter your name.");
+ callback(name);
+ }
+
+ outerFunction(callbackFunction);
+
+ `
+ },
+ {
+ slug: "why-do-we-need-callbacks",
+ question: "Why do we need callbacks",
+ answer: `The callbacks are needed because javascript is an event driven language. That means instead of waiting for a response javascript will keep executing while listening for other events. + Let's take an example with the first function invoking an API call(simulated by setTimeout) and the next function which logs the message.
+
+ function firstFunction() {
+ // Simulate a code delay
+ setTimeout(function () {
+ console.log("First function called");
+ }, 1000);
+ }
+ function secondFunction() {
+ console.log("Second function called");
+ }
+ firstFunction();
+ secondFunction();
+
+ Output;
+ // Second function called
+ // First function called
+
+ As observed from the output, javascript didn't wait for the response of the first function and the remaining code block got executed. So callbacks are used in a way to make sure that certain code doesn’t execute until the other code finishes execution.
+ ` + }, + { + slug: "what-is-a-callback-hell", + question: "What is a callback hell", + answer: `Callback Hell is an anti-pattern with multiple nested callbacks which makes code hard to read and debug when dealing with asynchronous logic. The callback hell looks like below,
+
+ async1(function(){
+ async2(function(){
+ async3(function(){
+ async4(function(){
+ ....
+ });
+ });
+ });
+ });
+
+ `
+ },{
+ slug: "can-we-define-properties-for-functions",
+ question: "Can we define properties for functions",
+ answer: `Yes, We can define properties for functions because functions are also objects.
+
+ fn = function (x) {
+ //Function code goes here
+ };
+
+ fn.name = "John";
+
+ fn.profile = function (y) {
+ //Profile code goes here
+ };
+
+ `
+ },
+ {
+ slug: "what-is-the-way-to-find-the-number-of-parameters-expected-by-a-function",
+ question: "What is the way to find the number of parameters expected by a function",
+ answer: `You can use function.length
syntax to find the number of parameters expected by a function. Let's take an example of sum
function to calculate the sum of numbers,
+ function sum(num1, num2, num3, num4) {
+ return num1 + num2 + num3 + num4;
+ }
+ sum.length; // 4 is the number of parameters expected.
+
+ `
+ },{
+ slug: "what-is-a-rest-parameter",
+ question: "What is a rest parameter",
+ answer: `Rest parameter is an improved way to handle function parameters which allows us to represent an indefinite number of arguments as an array. The syntax would be as below,
+
+ function f(a, b, ...theArgs) {
+ // ...
+ }
+
+ For example, let's take a sum example to calculate on dynamic number of parameters,
+
+ function sum(...args) {
+ let total = 0;
+ for (const i of args) {
+ total += i;
+ }
+ return total;
+ }
+
+ console.log(sum(1, 2)); //3
+ console.log(sum(1, 2, 3)); //6
+ console.log(sum(1, 2, 3, 4)); //13
+ console.log(sum(1, 2, 3, 4, 5)); //15
+
+ Note: Rest parameter is added in ES2015 or ES6
+ ` + }, + { + slug: "what-happens-if-you-do-not-use-rest-parameter-as-a-last-argument", + question: "What happens if you do not use rest parameter as a last argument", + answer: `The rest parameter should be the last argument, as its job is to collect all the remaining arguments into an array. For example, if you define a function like below it doesn’t make any sense and will throw an error.
+
+ function someFunc(a,…b,c){
+ //You code goes here
+ return;
+ }
+
+ `
+ },{
+ slug: "what-is-an-anonymous-function",
+ question: "What is an anonymous function",
+ answer: `An anonymous function is a function without a name! Anonymous functions are commonly assigned to a variable name or used as a callback function. The syntax would be as below,
+
+ function (optionalParameters) {
+ //do something
+ }
+
+ const myFunction = function(){ //Anonymous function assigned to a variable
+ //do something
+ };
+
+ [1, 2, 3].map(function(element){ //Anonymous function used as a callback function
+ //do something
+ });
+
+ Let's see the above anonymous function in an example,
+
+ var x = function (a, b) {
+ return a * b;
+ };
+ var z = x(5, 10);
+ console.log(z); // 50
+
+ `
+ },{
+ slug: "what-are-the-function-parameter-rules",
+ question: "What are the function parameter rules",
+ answer: `JavaScript functions follow below rules for parameters,
+i.e, The below function follows the above rules,
+
+ function functionName(parameter1, parameter2, parameter3) {
+ console.log(parameter1); // 1
+ }
+ functionName(1);
+
+ `
+ },{
+ slug: "what-are-default-parameters",
+ question: "What are default parameters",
+ answer: `In ES5, we need to depend on logical OR operators to handle default values of function parameters. Whereas in ES6, Default function parameters feature allows parameters to be initialized with default values if no value or undefined is passed. Let's compare the behavior with examples,
+
+ //ES5
+ var calculateArea = function (height, width) {
+ height = height || 50;
+ width = width || 60;
+ return width * height;
+ };
+ console.log(calculateArea()); //300
+
+ The default parameters make the initialization simpler in ES6:
+
+ //ES6
+ var calculateArea = function (height = 50, width = 60) {
+ return width * height;
+ };
+ console.log(calculateArea()); //300
+
+ `
+ },{
+ slug: "what-is-the-difference-between-parameter-and-argument",
+ question: "What is the difference between a parameter and an argument",
+ answer: `A parameter is the variable name of a function definition, whereas an argument represents the value given to a function when it is invoked. Let's explain this with a simple function:
+
+ function myFunction(parameter1, parameter2, parameter3) {
+ console.log(arguments[0]); // "argument1"
+ console.log(arguments[1]); // "argument2"
+ console.log(arguments[2]); // "argument3"
+ }
+ myFunction("argument1", "argument2", "argument3");
+
+ `
+ },{
+ slug: "what-is-a-thunk-function",
+ question: "What is a thunk function",
+ answer: `A thunk is just a function that delays the evaluation of the value. It doesn’t take any arguments but gives the value whenever you invoke the thunk. That is, it is used not to execute now but sometime in the future. Let's take a synchronous example:
+
+ const add = (x, y) => x + y;
+
+ const thunk = () => add(2, 3);
+
+ thunk(); // 5
+
+ `
+ },
+ {
+ slug: "what-are-asynchronous-thunks",
+ question: "What are asynchronous thunks",
+ answer: `Asynchronous thunks are useful for making network requests. Let's see an example of network requests:
+
+ function fetchData(fn) {
+ fetch("https://jsonplaceholder.typicode.com/todos/1")
+ .then((response) => response.json())
+ .then((json) => fn(json));
+ }
+
+ const asyncThunk = function () {
+ return fetchData(function getData(data) {
+ console.log(data);
+ });
+ };
+
+ asyncThunk();
+
+ The getData
function won't be called immediately, but it will be invoked only when the data is available from the API endpoint. The setTimeout
function is also used to make our code asynchronous. A real-life example is the Redux state management library, which uses asynchronous thunks to delay actions before dispatching them.
The functions created with the Function constructor
do not create closures to their creation contexts, but they are always created in the global scope. That is, the function can access its own local variables and global scope variables only. On the other hand, function declarations can access outer function variables (closures) too.
Let's see this difference with an example:
+var a = 100;
+ function createFunction() {
+ var a = 200;
+ return new Function("return a;");
+ }
+ console.log(createFunction()()); // 100
+
+
+ var a = 100;
+ function createFunction() {
+ var a = 200;
+ return function func() {
+ return a;
+ };
+ }
+ console.log(createFunction()()); // 200
+
+ `
+ },
+ {
+ slug: "what-are-the-differences-between-pure-and-impure-functions",
+ question: "What are the differences between pure and impure functions?",
+ answer: `Pure Functions:
+
+ function add(a, b) {
+ return a + b;
+ }
+
+ Impure Functions:
+
+ let total = 0;
+
+ function addToTotal(x) {
+ total += x;
+ console.log(total); // Side effect: Logging
+ }
+
+
+ Pure function | +Impure function | +
---|---|
It has no side effects | +It causes side effects | +
It always returns the same result | +It returns a different result on each call | +
Easy to read and debug | +Difficult to read and debug because they are affected by external code | +
push(): This method adds one or more elements to the end of an array and returns the new length of the array. It modifies the original array.
+
+ let arr = [1, 2, 3];
+ arr.push(4); // arr is now [1, 2, 3, 4]
+
+ pop(): The pop()
method removes the last element from an array and returns that element. It modifies the original array.
+ let arr = [1, 2, 3];
+ let poppedElement = arr.pop(); // arr is now [1, 2], poppedElement is 3
+
+ shift(): This method removes the first element from an array and returns that removed element. It also shifts all remaining elements to a lower index. It modifies the original array.
+
+ let arr = [1, 2, 3];
+ let shiftedElement = arr.shift(); // arr is now [2, 3], shiftedElement is 1
+
+ unshift(): The unshift()
method adds one or more elements to the beginning of an array and returns the new length of the array. It modifies the original array.
+ let arr = [2, 3];
+ arr.unshift(1); // arr is now [1, 2, 3]
+
+ concat(): This method is used to merge two or more arrays. It does not modify the existing arrays, but instead returns a new array that contains elements from the original arrays.
+
+ array1.concat(array2, array3, ..., arrayX)
+ let arr1 = [1, 2];
+ let arr2 = [3, 4];
+ let newArr = arr1.concat(arr2); // newArr is [1, 2, 3, 4]
+
+ slice(): The slice()
method returns a shallow copy of a portion of an array into a new array object. The original array will not be modified. It takes two arguments: the start index (inclusive) and the end index (exclusive).
+ let arr = [1, 2, 3, 4, 5];
+ let slicedArr = arr.slice(1, 4); // slicedArr is [2, 3, 4]
+
+ splice(): This method changes the contents of an array by removing or replacing existing elements and/or adding new elements in place. It modifies the original array and returns an array containing the deleted elements, if any.
+
+ let arr = [1, 2, 3, 4, 5];
+ arr.splice(2, 1); // arr is now [1, 2, 4, 5], removed element is [3]
+
+ forEach(): The forEach()
method executes a provided function once for each array element. It does not return a new array. It is commonly used for performing a function on each element of an array.
+ let arr = [1, 2, 3];
+ arr.forEach(item => console.log(item)); // outputs: 1, 2, 3
+
+ filter(): The filter()
method creates a new array with all elements that pass the test implemented by the provided function. It does not mutate the original array.
+ let numbers = [1, 2, 3, 4, 5];
+ let evenNumbers = numbers.filter(num => num % 2 === 0); // evenNumbers is [2, 4]
+
+ map(): The map()
method creates a new array populated with the results of calling a provided function on every element in the calling array. It does not mutate the original array.
+ let numbers = [1, 2, 3];
+ let doubledNumbers = numbers.map(num => num * 2); // doubledNumbers is [2, 4, 6]
+
+ reduce(): The reduce()
method executes a reducer function (that you provide) on each element of the array, resulting in a single output value. It can be used to perform calculations or transformations on an array.
+ let numbers = [1, 2, 3, 4];
+ let sum = numbers.reduce((acc, curr) => acc + curr, 0); // sum is 10
+
+ some(): The some()
method tests whether at least one element in the array passes the test implemented by the provided function. It returns true
if at least one element passes the test, otherwise false
.
+ let numbers = [1, 2, 3, 4, 5];
+ let hasEvenNumber = numbers.some(num => num % 2 === 0); // true
+
+ every(): The every()
method tests whether all elements in the array pass the test implemented by the provided function. It returns true
if all elements pass the test, otherwise false
.
+ let numbers = [2, 4, 6, 8];
+ let allEvenNumbers = numbers.every(num => num % 2 === 0); // true
+
+ indexOf(): The indexOf()
method returns the first index at which a given element can be found in the array, or -1 if it is not present.
+ let fruits = ['apple', 'banana', 'orange'];
+ let index = fruits.indexOf('banana'); // index is 1
+
+ includes(): The includes()
method determines whether an array includes a certain value among its entries, returning true
or false
as appropriate.
+ let fruits = ['apple', 'banana', 'orange'];
+ let hasBanana = fruits.includes('banana'); // true
+
+ sort(): The sort()
method sorts the elements of an array in place and returns the sorted array. By default, it sorts the elements as strings in Unicode code point order. The sort()
method modifies the original array and returns a reference to the same array.
+ let fruits = ['banana', 'apple', 'orange'];
+ fruits.sort(); // fruits is now ['apple', 'banana', 'orange']
+
+ To sort numbers in ascending order, you need to provide a comparison function.
+
+ let numbers = [3, 1, 4, 1, 5, 9, 2, 6];
+ numbers.sort((a, b) => a - b); // numbers is now [1, 1, 2, 3, 4, 5, 6, 9]
+
+ find(): The find()
method returns the value of the first element in the array that satisfies the provided testing function. Otherwise, it returns undefined
. It doesn't mutate the array.
+ let numbers = [1, 2, 3, 4, 5];
+ let found = numbers.find(num => num > 3); // found is 4
+
+ reverse(): The reverse()
method reverses the elements of an array in place, modifying the original array, and returns a reference to the same array.
+ let numbers = [1, 2, 3, 4, 5];
+ numbers.reverse(); // numbers is now [5, 4, 3, 2, 1]
+
+ Some of the major differences in a tabular form:
+Slice | +Splice | +
---|---|
Doesn't modify the original array (immutable) | +Modifies the original array (mutable) | +
Returns the subset of the original array | +Returns the deleted elements as an array | +
Used to pick the elements from the array | +Used to insert/delete elements to/from the array | +
You can use Math.min
and Math.max
methods on array variables to find the minimum and maximum elements within an array. Let's create two functions to find the min and max value within an array:
+ var marks = [50, 20, 70, 60, 45, 30];
+ function findMin(arr) {
+ return Math.min.apply(null, arr);
+ }
+ function findMax(arr) {
+ return Math.max.apply(null, arr);
+ }
+
+ console.log(findMin(marks));
+ console.log(findMax(marks));
+
+ `
+ },{
+ slug: "how-do-you-compare-scalar-arrays",
+ question: "How do you compare scalar arrays?",
+ answer: `You can use the length
and every
methods of arrays to compare two scalar arrays (compared directly using ===
). The combination of these expressions can give the expected result:
+ const arrayFirst = [1, 2, 3, 4, 5];
+ const arraySecond = [1, 2, 3, 4, 5];
+ console.log(
+ arrayFirst.length === arraySecond.length &&
+ arrayFirst.every((value, index) => value === arraySecond[index])
+ ); // true
+
+ If you would like to compare arrays irrespective of order, then you should sort them before:
+
+ const arrayFirst = [2, 3, 1, 4, 5];
+ const arraySecond = [1, 2, 3, 4, 5];
+ console.log(
+ arrayFirst.length === arraySecond.length &&
+ arrayFirst.sort().every((value, index) => value === arraySecond[index])
+ ); // true
+
+ `
+ },{
+ slug: "what-is-for-of-statement",
+ question: "What is for...of statement?",
+ answer: `The for...of
statement creates a loop iterating over iterable objects or elements such as built-in String, Array, Array-like objects (like arguments or NodeList), TypedArray, Map, Set, and user-defined iterables. The basic usage of for...of
statement on arrays would be as below:
+ let arrayIterable = [10, 20, 30, 40, 50];
+
+ for (let value of arrayIterable) {
+ value++;
+ console.log(value); // 11 21 31 41 51
+ }
+
+ `
+ },{
+ slug: "what-happens-with-negating-an-array",
+ question: "What happens with negating an array?",
+ answer: `Negating an array with the !
character will coerce the array into a boolean. Since Arrays are considered to be truthy, negating it will return false
.
+ console.log(![]); // false
+
+ `
+ },
+ {
+ slug: "what-happens-if-we-add-two-arrays",
+ question: "What happens if we add two arrays?",
+ answer: `If you add two arrays together, it will convert them both to strings and concatenate them. For example, the result of adding arrays would be as below:
+
+ console.log(["a"] + ["b"]); // "ab"
+ console.log([] + []); // ""
+ console.log(![] + []); // "false", because ![] returns false.
+
+ `
+ },
+ {
+ slug: "how-do-you-remove-falsy-values-from-an-array",
+ question: "How do you remove falsy values from an array?",
+ answer: `You can apply the filter
method on the array by passing Boolean
as a parameter. This way, it removes all falsy values (0, undefined, null, false, and "") from the array.
+ const myArray = [false, null, 1, 5, undefined];
+ myArray.filter(Boolean); // [1, 5] // is same as myArray.filter(x => x);
+
+ `
+ },
+ {
+ slug: "how-do-you-get-unique-values-of-an-array",
+ question: "How do you get unique values of an array?",
+ answer: `You can get unique values of an array with the combination of Set
and rest expression/spread(...) syntax.
+ console.log([...new Set([1, 2, 4, 4, 3])]); // [1, 2, 4, 3]
+
+ `
+ },
+ {
+ slug: "what-is-the-easiest-way-to-convert-an-array-to-an-object",
+ question: "What is the easiest way to convert an array to an object?",
+ answer: `You can convert an array to an object with the same data using the spread (...) operator.
+
+ var fruits = ["banana", "apple", "orange", "watermelon"];
+ var fruitsObject = { ...fruits };
+ console.log(fruitsObject); // {0: "banana", 1: "apple", 2: "orange", 3: "watermelon"}
+
+ `
+ },
+ {
+ slug: "how-do-you-create-an-array-with-some-data",
+ question: "How do you create an array with some data?",
+ answer: `You can create an array with some data or an array with the same values using the fill
method.
+ var newArray = new Array(5).fill("0");
+ console.log(newArray); // ["0", "0", "0", "0", "0"]
+
+ `
+ },{
+ slug: "how-do-you-flatten-multi-dimensional-arrays",
+ question: "How do you flattening multi-dimensional arrays?",
+ answer: `Flattening bi-dimensional arrays is trivial with the spread operator:
+
+ const biDimensionalArr = [11, [22, 33], [44, 55], [66, 77], 88, 99];
+ const flattenArr = [].concat(...biDimensionalArr); // [11, 22, 33, 44, 55, 66, 77, 88, 99]
+
+ But you can make it work with multi-dimensional arrays by recursive calls:
+
+ function flattenMultiArray(arr) {
+ const flattened = [].concat(...arr);
+ return flattened.some((item) => Array.isArray(item))
+ ? flattenMultiArray(flattened)
+ : flattened;
+ }
+ const multiDimensionalArr = [11, [22, 33], [44, [55, 66, [77, [88]], 99]]];
+ const flatArr = flattenMultiArray(multiDimensionalArr); // [11, 22, 33, 44, 55, 66, 77, 88, 99]
+
+ Also, you can use the flat
method of Array:
+ const arr = [1, [2, 3], 4, 5, [6, 7]];
+ const fllattenArr = arr.flat(); // [1, 2, 3, 4, 5, 6, 7]
+
+ // For multi-dimensional arrays
+ const multiDimensionalArr = [11, [22, 33], [44, [55, 66, [77, [88]], 99]]];
+ const oneStepFlat = multiDimensionalArr.flat(1); // [11, 22, 33, 44, [55, 66, [77, [88]], 99]]
+ const towStep = multiDimensionalArr.flat(2); // [11, 22, 33, 44, 55, 66, [77, [88]], 99]
+ const fullyFlatArray = multiDimensionalArr.flat(Infinity); // [11, 22, 33, 44, 55, 66, 77, 88, 99]
+
+ `
+ },
+ {
+ slug: "what-is-the-difference-between-dense-and-sparse-arrays",
+ question: "What is the difference between dense and sparse arrays?",
+ answer: `An array contains items at each index starting from the first (0) to the last (array.length - 1
) is called a Dense array. Whereas if at least one item is missing at any index, the array is called sparse.
Let's see the below two kinds of arrays:
+
+ const avengers = ["Ironman", "Hulk", "CaptainAmerica"];
+ console.log(avengers[0]); // 'Ironman'
+ console.log(avengers[1]); // 'Hulk'
+ console.log(avengers[2]); // 'CaptainAmerica'
+ console.log(avengers.length); // 3
+
+ const justiceLeague = ["Superman", "Aquaman", , "Batman"];
+ console.log(justiceLeague[0]); // 'Superman'
+ console.log(justiceLeague[1]); // 'Aquaman'
+ console.log(justiceLeague[2]); // undefined
+ console.log(justiceLeague[3]); // 'Batman'
+ console.log(justiceLeague.length); // 4
+
+ `
+ },
+ {
+ slug: "what-are-the-different-ways-to-create-sparse-arrays",
+ question: "What are the different ways to create sparse arrays?",
+ answer: `There are 4 different ways to create sparse arrays in JavaScript:
+
+ const justiceLeague = ["Superman", "Aquaman", , "Batman"];
+ console.log(justiceLeague); // ['Superman', 'Aquaman', empty ,'Batman']
+
+ Array(length)
or new Array(length)
+ const array = Array(3);
+ console.log(array); // [empty, empty ,empty]
+
+ delete array[index]
operator on the array
+ const justiceLeague = ["Superman", "Aquaman", "Batman"];
+ delete justiceLeague[1];
+ console.log(justiceLeague); // ['Superman', empty, ,'Batman']
+
+
+ const justiceLeague = ["Superman", "Aquaman", "Batman"];
+ justiceLeague.length = 5;
+ console.log(justiceLeague); // ['Superman', 'Aquaman', 'Batman', empty, empty]
+
+ The reverse()
method reverses the order of the elements in an array, but it mutates the original array. However, there are several solutions that won't mutate the original array:
slice()
method on the array to create a shallow copy followed by the reverse()
method call on the copy.
+ const originalArray = [1, 2, 3, 4, 5];
+ const newArray = originalArray.slice().reverse(); //Slice an array gives a new copy
+
+ console.log(originalArray); // [1, 2, 3, 4, 5]
+ console.log(newArray); // [ 5, 4, 3, 2, 1]
+
+ reverse()
method call on the copy.
+ const originalArray = [1, 2, 3, 4, 5];
+ const newArray = [...originalArray].reverse();
+
+ console.log(originalArray); // [1, 2, 3, 4, 5]
+ console.log(newArray); // [ 5, 4, 3, 2, 1]
+
+
+ const originalArray = [1, 2, 3, 4, 5];
+ const newArray = originalArray.reduce((accumulator, value) => {
+ return [value, ...accumulator];
+ }, []);
+
+ console.log(originalArray); // [1, 2, 3, 4, 5]
+ console.log(newArray); // [ 5, 4, 3, 2, 1]
+
+
+ const originalArray = [1, 2, 3, 4, 5];
+ const newArray = originalArray.reduceRight((accumulator, value) => {
+ return [...accumulator, value];
+ }, []);
+
+ console.log(originalArray); // [1, 2, 3, 4, 5]
+ console.log(newArray); // [ 5, 4, 3, 2, 1]
+
+
+ const originalArray = [1, 2, 3, 4, 5];
+ const newArray = originalArray.reduceRight((accumulator, value) => {
+ accumulator.push(value);
+ return accumulator;
+ }, []);
+
+ console.log(originalArray); // [1, 2, 3, 4, 5]
+ console.log(newArray); // [ 5, 4, 3, 2, 1]
+
+ It is possible to check if a variable is an array instance using 3 different ways:
+Array.isArray(value)
utility function is used to determine whether value
is an array or not. This function returns a true boolean value if the variable is an array and a false value if it is not.
+ const numbers = [1, 2, 3];
+ const user = { name: "John" };
+ Array.isArray(numbers); // true
+ Array.isArray(user); //false
+
+ instanceof
operator is used to check the type of an array at runtime. It returns true if the type of a variable is an Array, otherwise false for other types.
+ const numbers = [1, 2, 3];
+ const user = { name: "John" };
+ console.log(numbers instanceof Array); // true
+ console.log(user instanceof Array); // false
+
+ constructor
property of the variable is used to determine whether the variable is of Array type or not.
+ const numbers = [1, 2, 3];
+ const user = { name: "John" };
+ console.log(numbers.constructor === Array); // true
+ console.log(user.constructor === Array); // false
+
+ There are many ways to create objects in JavaScript as mentioned below:
+
+ var object = {
+ name: "Sudheer",
+ age: 34
+ };
+
+ Object
constructor. Currently, this approach is not recommended.
+ var object = new Object();
+
+
+ var object = Object.create(null);
+
+ new
operator to create object instances.
+ function Person(name) {
+ this.name = name;
+ this.age = 21;
+ }
+ var object = new Person("Sudheer");
+
+
+ function Person() {}
+ Person.prototype.name = "Sudheer";
+ var object = new Person();
+
+
+ class Person {
+ constructor(name) {
+ this.name = name;
+ }
+ }
+
+ var object = new Person("Sudheer");
+
+
+ var object = new (function () {
+ this.name = "Sudheer";
+ })();
+
+ Prototype chaining is used to build new types of objects based on existing ones. It is similar to inheritance in a class-based language.
+The prototype on an object instance is available through Object.getPrototypeOf(object)
or __proto__
property, whereas the prototype on constructor functions is available through Object.prototype
.
The difference between Call, Apply, and Bind can be explained with the examples below:
+Call: The call()
method invokes a function with a given this
value and arguments provided one by one.
+ var employee1 = { firstName: "John", lastName: "Rodson" };
+ var employee2 = { firstName: "Jimmy", lastName: "Baily" };
+
+ function invite(greeting1, greeting2) {
+ console.log(
+ greeting1 + " " + this.firstName + " " + this.lastName + ", " + greeting2
+ );
+ }
+
+ invite.call(employee1, "Hello", "How are you?"); // Hello John Rodson, How are you?
+ invite.call(employee2, "Hello", "How are you?"); // Hello Jimmy Baily, How are you?
+
+ Apply: Invokes the function with a given this
value and allows you to pass in arguments as an array.
+ var employee1 = { firstName: "John", lastName: "Rodson" };
+ var employee2 = { firstName: "Jimmy", lastName: "Baily" };
+
+ function invite(greeting1, greeting2) {
+ console.log(
+ greeting1 + " " + this.firstName + " " + this.lastName + ", " + greeting2
+ );
+ }
+
+ invite.apply(employee1, ["Hello", "How are you?"]); // Hello John Rodson, How are you?
+ invite.apply(employee2, ["Hello", "How are you?"]); // Hello Jimmy Baily, How are you?
+
+ Bind: Returns a new function, allowing you to pass any number of arguments.
+
+ var employee1 = { firstName: "John", lastName: "Rodson" };
+ var employee2 = { firstName: "Jimmy", lastName: "Baily" };
+
+ function invite(greeting1, greeting2) {
+ console.log(
+ greeting1 + " " + this.firstName + " " + this.lastName + ", " + greeting2
+ );
+ }
+
+ var inviteEmployee1 = invite.bind(employee1);
+ var inviteEmployee2 = invite.bind(employee2);
+ inviteEmployee1("Hello", "How are you?"); // Hello John Rodson, How are you?
+ inviteEmployee2("Hello", "How are you?"); // Hello Jimmy Baily, How are you?
+
+ Call and Apply are pretty much interchangeable. Both execute the current function immediately. You need to decide whether it’s easier to send in an array or a comma-separated list of arguments. You can remember by treating Call is for comma (separated list) and Apply is for Array.
+Bind creates a new function that will have this
set to the first parameter passed to bind()
.
Objects are similar to Maps in that both let you set keys to values, retrieve those values, delete keys, and detect whether something is stored at a key. Due to this reason, Objects have been used as Maps historically. But there are important differences that make using a Map preferable in certain cases:
+Object.create(null)
, but this practice is seldom done.Native objects are objects that are part of the JavaScript language defined by the ECMAScript specification. For example, String, Math, RegExp, Object, Function etc core objects defined in the ECMAScript spec.
+Host objects are objects provided by the browser or runtime environment (Node). For example, window, XmlHttpRequest, DOM nodes etc are considered as host objects.
+User objects are objects defined in the javascript code. For example, User objects created for profile information.
+ ` + },{ + slug: "how-to-check-if-a-key-exists-in-an-object", + question: "How do you check if a key exists in an object", + answer: ` +You can check whether a key exists in an object or not using three approaches:
+in
operator to check whether a key exists in an object or not.
+ "key" in obj;
+
+ If you want to check if a key doesn't exist, remember to use parentheses:
+
+ !("key" in obj);
+
+ hasOwnProperty
to particularly test for properties of the object instance (and not inherited properties).
+ obj.hasOwnProperty("key"); // true
+
+
+ const user = {
+ name: "John",
+ };
+
+ console.log(user.name !== undefined); // true
+ console.log(user.nickName !== undefined); // false
+
+ You can use the for-in
loop to loop through a JavaScript object. You can also make sure that the key you get is an actual property of an object and doesn't come from the prototype using the hasOwnProperty
method.
+ var object = {
+ k1: "value1",
+ k2: "value2",
+ k3: "value3",
+ };
+
+ for (var key in object) {
+ if (object.hasOwnProperty(key)) {
+ console.log(key + " -> " + object[key]); // k1 -> value1 ...
+ }
+ }
+
+ `
+ },{
+ slug: "how-to-test-for-an-empty-object",
+ question: "How do you test for an empty object",
+ answer: `
+ There are different solutions based on ECMAScript versions:
+
+ Object.entries(obj).length === 0 && obj.constructor === Object;
+ // Since date object length is 0, you need to check constructor check as well
+
+
+ Object.keys(obj).length === 0 && obj.constructor === Object;
+ // Since date object length is 0, you need to check constructor check as well
+
+
+ function isEmpty(obj) {
+ for (var prop in obj) {
+ if (obj.hasOwnProperty(prop)) {
+ return false;
+ }
+ }
+
+ return JSON.stringify(obj) === JSON.stringify({});
+ }
+
+ It is recommended to avoid creating new objects using new Object()
. Instead, you can initialize values based on their type to create the objects.
{}
instead of new Object()
""
instead of new String()
0
instead of new Number()
false
instead of new Boolean()
[]
instead of new Array()
/()/
instead of new RegExp()
function (){};
instead of new Function()
You can define them as follows:
+
+ var v1 = {};
+ var v2 = "";
+ var v3 = 0;
+ var v4 = false;
+ var v5 = [];
+ var v6 = /()/;
+ var v7 = function () {};
+
+ `
+ },{
+ slug: "what-is-the-difference-between-proto-and-prototype",
+ question: "What is the difference between proto and prototype",
+ answer: `
+ The __proto__
object is the actual object that is used in the lookup chain to resolve methods, etc., whereas prototype
is the object that is used to build __proto__
when you create an object with new
.
+ new Employee().__proto__ === Employee.prototype;
+ new Employee().prototype === undefined;
+
+ There are a few more differences:
+Feature | +Prototype | +proto | +
---|---|---|
Access | +All the function constructors have prototype properties. | +All the objects have __proto__ property. |
+
Purpose | +Used to reduce memory wastage with a single copy of function. | +Used in lookup chain to resolve methods, constructors, etc. | +
ECMAScript | +Introduced in ES6 | +Introduced in ES5 | +
Usage | +Frequently used | +Rarely used | +
The freeze() method is used to freeze an object. Freezing an object does not allow adding new properties to an object, prevents from removing, and prevents changing the enumerability, configurability, or writability of existing properties. It returns the passed object and does not create a frozen copy.
+
+ const obj = {
+ prop: 100,
+ };
+
+ Object.freeze(obj);
+ obj.prop = 200; // Throws an error in strict mode
+
+ console.log(obj.prop); //100
+
+ Remember freezing is only applied to the top-level properties in objects but not for nested objects. For example, let's try to freeze a user object which has employment details as a nested object and observe that details have been changed.
+
+ const user = {
+ name: "John",
+ employment: {
+ department: "IT",
+ },
+ };
+
+ Object.freeze(user);
+ user.employment.department = "HR";
+
+ Note: It causes a TypeError if the argument passed is not an object.
+ ` + },{ + slug: "purpose-and-need-for-freeze-method", + question: "What is the purpose of freeze method", + answer: ` +Below are the main benefits of using the freeze method:
+In the Object-oriented paradigm, an existing API contains certain elements that are not intended to be extended, modified, or re-used outside of their current context. Hence it works as the final
keyword which is used in various languages.
The Object.isFrozen()
method is used to determine if an object is frozen or not. An object is frozen if all of the below conditions hold true:
The usage is as follows:
+
+ const object = {
+ property: "Welcome JS world",
+ };
+ Object.freeze(object);
+ console.log(Object.isFrozen(object));
+
+ `,
+ },
+ {
+ slug: "how-to-determine-whether-object-is-frozen-or-not-and-compare-values-using-object-is",
+ question: "How do you determine two values same or not using object",
+ answer: `
+ The Object.is()
method determines whether two values are the same value. For example, the usage with different types of values would be:
+ Object.is("hello", "hello"); // true
+ Object.is(window, window); // true
+ Object.is([], []); // false
+
+ Two values are the same if one of the following holds:
+Some of the applications of Object's is
method are as follows:
You can use the Object.assign()
method, which is used to copy the values and properties from one or more source objects to a target object. It returns the target object which has properties and values copied from the source objects. The syntax would be as follows:
+ Object.assign(target, ...sources);
+
+ Let's take an example with one source and one target object:
+
+ const target = { a: 1, b: 2 };
+ const source = { b: 3, c: 4 };
+
+ const returnedTarget = Object.assign(target, source);
+
+ console.log(target); // { a: 1, b: 3, c: 4 }
+
+ console.log(returnedTarget); // { a: 1, b: 3, c: 4 }
+
+ As observed in the above code, there is a common property (b
) from source to target so its value has been overwritten.
Below are some of the main applications of Object.assign()
method:
The Proxy object is used to define custom behavior for fundamental operations such as property lookup, assignment, enumeration, function invocation, etc. The syntax would be as follows:
+
+ var p = new Proxy(target, handler);
+
+ Let's take an example of a proxy object:
+
+ var handler = {
+ get: function (obj, prop) {
+ return prop in obj ? obj[prop] : 100;
+ },
+ };
+
+ var p = new Proxy({}, handler);
+ p.a = 10;
+ p.b = null;
+
+ console.log(p.a, p.b); // 10, null
+ console.log("c" in p, p.c); // false, 100
+
+ In the above code, it uses get
handler which defines the behavior of the proxy when an operation is performed on it.
The Object.seal() method is used to seal an object, by preventing new properties from being added to it and marking all existing properties as non-configurable. But values of present properties can still be changed as long as they are writable. Let's see the below example to understand more about seal()
method:
+ const object = {
+ property: "Welcome JS world",
+ };
+ Object.seal(object);
+ object.property = "Welcome to object world";
+ console.log(Object.isSealed(object)); // true
+ delete object.property; // You cannot delete when sealed
+ console.log(object.property); // Welcome to object world
+
+ `
+ },{
+ slug: "what-are-the-applications-of-seal-method",
+ question: "What are the applications of seal method",
+ answer: `
+ Below are the main applications of Object.seal()
method:
If an object is frozen using the Object.freeze()
method then its properties become immutable and no changes can be made in them, whereas if an object is sealed using the Object.seal()
method then the changes can be made in the existing properties of the object.
The Object.isSealed()
method is used to determine if an object is sealed or not. An object is sealed if all of the below conditions hold true:
Let's see it in action:
+
+ const object = {
+ property: "Hello, Good morning",
+ };
+
+ Object.seal(object); // Using seal() method to seal the object
+
+ console.log(Object.isSealed(object)); // checking whether the object is sealed or not
+
+ `
+ },
+ {
+ slug: "how-to-get-enumerable-key-and-value-pairs",
+ question: "How do you get enumerable key and value pairs",
+ answer: `
+ The Object.entries()
method is used to return an array of a given object's own enumerable string-keyed property [key, value] pairs, in the same order as that provided by a for...in
loop. Let's see the functionality of Object.entries()
method in an example:
+ const object = {
+ a: "Good morning",
+ b: 100,
+ };
+
+ for (let [key, value] of Object.entries(object)) {
+ console.log(\`\${key}: \${value}\`); // a: 'Good morning'
+ // b: 100
+ }
+
+ Note: The order is not guaranteed as object defined.
+ ` + },{ + slug: "what-is-the-main-difference-between-object-values-and-object-entries-method", + question: "What is the main difference between Object.values and Object.entries method", + answer: ` +The Object.values()
method's behavior is similar to Object.entries()
method but it returns an array of values instead of [key, value] pairs.
+ const object = {
+ a: "Good morning",
+ b: 100,
+ };
+
+ for (let value of Object.values(object)) {
+ console.log(\`\${value}\`); // 'Good morning'
+ // 100
+ }
+
+ `
+ },{
+ slug: "how-to-get-list-of-keys-of-any-object",
+ question: "How can you get the list of keys of any object",
+ answer: `
+ You can use the Object.keys()
method, which is used to return an array of a given object's own property names, in the same order as we get with a normal loop. For example, you can get the keys of a user object:
+ const user = {
+ name: "John",
+ gender: "male",
+ age: 40,
+ };
+
+ console.log(Object.keys(user)); // ['name', 'gender', 'age']
+
+ `
+ },{
+ slug: "how-to-create-an-object-with-prototype",
+ question: "How do you create an object with prototype",
+ answer: `
+ The Object.create()
method is used to create a new object with the specified prototype object and properties. It uses an existing object as the prototype of the newly created object. It returns a new object with the specified prototype object and properties.
+ const user = {
+ name: "John",
+ printInfo: function () {
+ console.log(\`My name is \${this.name}.\`);
+ },
+ };
+
+ const admin = Object.create(user);
+
+ admin.name = "Nick"; // Remember that "name" is a property set on "admin" but not on "user" object
+
+ admin.printInfo(); // My name is Nick
+
+ `
+ },{
+ slug: "what-are-the-different-ways-to-access-object-properties",
+ question: "What are the different ways to access object properties",
+ answer: `
+ There are 3 possible ways for accessing the property of an object:
+
+ objectName.property;
+
+
+ objectName["property"];
+
+
+ objectName[expression];
+
+ You can use the Object.getPrototypeOf(obj)
method to return the prototype of the specified object. i.e. The value of the internal prototype
property. If there are no inherited properties then null
value is returned.
+ const newPrototype = {};
+ const newObject = Object.create(newPrototype);
+
+ console.log(Object.getPrototypeOf(newObject) === newPrototype); // true
+
+ `
+ },{
+ slug: "what-happens-if-i-pass-string-type-for-getprototype-method",
+ question: "What happens If I pass string type for getPrototype method",
+ answer: `
+ In ES5, it will throw a TypeError
exception if the obj
parameter isn't an object. Whereas in ES2015, the parameter will be coerced to an Object
.
+ // ES5
+ Object.getPrototypeOf("James"); // TypeError: "James" is not an object
+ // ES2015
+ Object.getPrototypeOf("James"); // String.prototype
+
+ `
+ },{
+ slug: "how-to-set-prototype-of-one-object-to-another",
+ question: "How do you set prototype of one object to another",
+ answer: `
+ You can use the Object.setPrototypeOf()
method that sets the prototype (i.e., the internal Prototype
property) of a specified object to another object or null. For example, if you want to set prototype of a square object to rectangle object would be as follows:
+ Object.setPrototypeOf(Square.prototype, Rectangle.prototype);
+ Object.setPrototypeOf({}, null);
+
+ `
+ },{
+ slug: "how-to-check-whether-an-object-can-be-extendable-or-not",
+ question: "How do you check whether an object can be extendable or not",
+ answer: `
+ The Object.isExtensible()
method is used to determine if an object is extendable or not. i.e., Whether it can have new properties added to it or not.
+ const newObject = {};
+ console.log(Object.isExtensible(newObject)); // true
+
+ Note: By default, all the objects are extendable. i.e., The new properties can be added or modified.
+ ` + },{ + slug: "what-are-the-different-ways-to-make-an-object-non-extensible", + question: "What are the different ways to make an object non-extensible", + answer: ` +You can mark an object non-extensible in 3 ways:
+Object.preventExtensions()
Object.seal()
Object.freeze()
+ var newObject = {};
+
+ Object.preventExtensions(newObject); // Prevent objects are non-extensible
+ Object.isExtensible(newObject); // false
+
+ var sealedObject = Object.seal({}); // Sealed objects are non-extensible
+ Object.isExtensible(sealedObject); // false
+
+ var frozenObject = Object.freeze({}); // Frozen objects are non-extensible
+ Object.isExtensible(frozenObject); // false
+
+ `
+ },{
+ slug: "what-is-json-and-its-common-operations",
+ question: "What is JSON and its common operations",
+ answer: `
+ JSON is a text-based data format following JavaScript object syntax, which was popularized by Douglas Crockford
. It is useful when you want to transmit data across a network. It is basically just a text file with an extension of .json, and a MIME type of application/json
Note: When exchanging data between a browser and a server, the data can only be text. Since JSON is text only, it can easily be sent to and from a server, and used as a data format by any programming language.
+ +Parsing: Converting a string to a native object
+
+ JSON.parse(text);
+
+
+ Stringification: Converting a native object to a string so that it can be transmitted across the network
+
+ JSON.stringify(object);
+
+ `
+ },{
+ slug: "what-are-the-syntax-rules-of-json",
+ question: "What are the syntax rules of JSON",
+ answer: `
+ Below are the list of syntax rules of JSON:
+Note: The ASCII values of the alphabet vary from 65 to 90 for uppercase letters and from 97 to 122 for lowercase letters.
+concat(): This method is used to concatenate one or more strings. It takes one or more strings as arguments and returns a new string that is the concatenation of the original string(s) and the argument(s).
+ +
+ let str1 = "Hello";
+ let str2 = "World";
+ let result = str1.concat(" ", str2);
+ console.log(result); // Output: Hello World
+
+
+charAt(): This method returns the character at a specified index in a string. It takes an index as an argument and returns the character at that index.
+ +
+ let str = "Hello";
+ console.log(str.charAt(0)); // Output: H
+ console.log(str.charAt(3)); // Output: l
+
+
+substring(): This method returns the part of the string between the specified start and end indexes. It takes one or two arguments: the start index and the end index.
+ +
+ let str = "Hello World";
+ console.log(str.substring(6)); // Output: World
+ console.log(str.substring(0, 5)); // Output: Hello
+
+
+slice(): This method extracts a section of a string and returns it as a new string. It takes one or two arguments: the start index and optionally the end index.
+ +
+ let str = "Hello World";
+ console.log(str.slice(6)); // Output: World
+ console.log(str.slice(0, 5)); // Output: Hello
+
+
+toUpperCase() / toLowerCase(): These methods return the string in uppercase or lowercase letters, respectively.
+ +
+ let str = "Hello World";
+ console.log(str.toUpperCase()); // Output: HELLO WORLD
+ console.log(str.toLowerCase()); // Output: hello world
+
+
+indexOf() / lastIndexOf(): These methods return the index of the first/last occurrence of a specified value in a string.
+ +
+ let str = "Hello World";
+ console.log(str.indexOf("o")); // Output: 4
+ console.log(str.lastIndexOf("o")); // Output: 7
+
+
+replace(): This method replaces a specified value with another value in a string.
+ +
+ let str = "Hello World";
+ console.log(str.replace("World", "Universe")); // Output: Hello Universe
+
+
+charCodeAt(): This method returns the Unicode value of the character at a specified index in a string.
+ +
+ let text = "Hello";
+ let code = text.charCodeAt(0);
+ console.log(code); // Output: 72 (Unicode value of 'H')
+
+
+fromCharCode(): This method returns a string created from the specified sequence of Unicode values.
+ +
+ let char = String.fromCharCode(65);
+ console.log(char); // Output: A
+
+
+split(): This method is used to split a string into an array of substrings based on a specified separator.
+
+ let str = "apple,banana,orange";
+ let arr = str.split(",");
+ console.log(arr); // Output: ["apple", "banana", "orange"]
+
+
+Here's a simple JavaScript function to reverse a string:
+
+ function reverseString(str) {
+ return str.split("").reverse().join("");
+ }
+
+ let originalString = "Hello";
+ let reversedString = reverseString(originalString);
+ console.log(reversedString); // Output: "olleH"
+
+
+A function that takes a string and a substring, and returns the number of times the substring appears within the string:
+
+ function countSubstring(str, subStr) {
+ // Calculate the length difference between the
+ // original string and the string after removing all occurrences of the substring
+ return (str.length - str.split(subStr).join('').length) / subStr.length;
+ }
+
+ // Example usage:
+ let mainString = "0101010101";
+ let subString = "010";
+ let count = countSubstring(mainString, subString);
+ console.log(count); // Output: 2
+
+
+
+
+ s.split("") // Convert the string s into an array of characters
+ .slice(Math.ceil(s.length / 2)) // Get the second half of the array
+ .reverse() // Reverse the order of the characters in the second half
+ .map((v, i) => {
+ //return Math.abs(s.charCodeAt(i) - v.charCodeAt(0));
+ // write your logic here
+ })
+
+
+
+Note: A string can be made into a palindrome if it has at most one character that occurs an odd number of times
+
+ function canBePalindrome(str) {
+ // Create a frequency map to count occurrences of characters
+ const frequencyMap = new Map();
+
+ // Count occurrences of each character in the string
+ for (let char of str) {
+ if (frequencyMap.has(char)) {
+ frequencyMap.set(char, frequencyMap.get(char) + 1);
+ } else {
+ frequencyMap.set(char, 1);
+ }
+ }
+
+ // Count how many characters occur an odd number of times
+ let oddCount = 0;
+ for (let count of frequencyMap.values()) {
+ if (count % 2 !== 0) {
+ oddCount++;
+ }
+ }
+
+ // A string can be made into a palindrome if it has at most one character that occurs an odd number of times
+ return oddCount <= 1;
+ }
+
+
+`
\ No newline at end of file
diff --git a/src/app/layout/home/home.component.html b/src/app/layout/home/home.component.html
index 66e3b9a..a7cbb5e 100644
--- a/src/app/layout/home/home.component.html
+++ b/src/app/layout/home/home.component.html
@@ -21,6 +21,18 @@ {{project.description!.length > 80 ? project.description!.substring(0,80)+'...' :project.description }}