Research and Study Data Structures in TypeScript:
Understand the commonly used data structures in TypeScript and how they are implemented.
Focus on how TypeScript’s strong typing system enhances the use of data structures.
Explain Each Data Structure in TypeScript:
For each data structure, provide the following details:
Definition: A brief explanation of the data structure.
- Data structures in typescript is very essential especially in holding of bunch of data. From the word data structure, commonly storing and holding bunch of data that need to be structured so it can be organized, and easily read and avoid code redundancy that may cause of slow latency in web development.
Key Features: The important characteristics and behaviors of the data structure.
- The important characteristics and behaviors of the data structure especially in web development is make our code or data be use organized, stored, and manipulated with a different approaches or method. It’s very useful not only to avoid code redundancy is because when we are accessing the specific value in our variable it’s easy to access for retrieve, display, and manipulating by using its index.
Use Cases: Where and why this data structure is typically used.
Most cases scenario use of data structure when you hold a bunch of data, and you don’t want to slow the run time especially in web development so, data structure is always best practice it. For instances, if you are storing collection of elements example of list of shoes, you can use array and access by its index starting in 0. It’s efficient when the collection of data was given in array.
Example of data structure using Array
Shoes: Strings [] = ["Nike","Adidas","Puma","Vans"];
In angular, you can use this you can use a *ngFor in html to access all elements and display, or *ngIf you want based on the input index number based. Read more for more detailed example.
Time Complexity: Analyze the performance of each data structure (Big-O notation) for common operations like insert, delete, and search.
In Array, insert data mostly inserting at the beginning or in the middle of the data collection, in delete operations, delete in beginning or in the middle, and search is in linear search based on their indices.
In Tuple, insert data in the middle, otherwise, it will be added the data insert or in short append. Delete operations deleting in the middle or in the beginning. and search is based on the linear search thorugh indices.
In ArrayList(Dynamic Arrays), Inserting data can be amortized or to expand the array but in the end or insert in the specific position, delete can remove in specific position, and search is also a linear search through indices.
In Stack, in inserting data, need to push() method which adds element to the end of the stack in O(1) time. O(1) means constantly amount of time regardless of how many you input data. Delete or pop() method use to remove last element to the stack in the O(1) time. and search using indexOf() which checks each element sequentially that lead to O(n) time complexity.
In Queue, using the Enqueue (called insert in Queue) push() method, add element of the queue, in O(1) time. Dequeue (called delete in Queue) shift() method removes the first element from the queue in O(1) time. and also, indexOf() use to check each element sequentially, leading of O(n) time complexity.
In LinkedList, Insert using insertAtHead() method inserts a new node at the beginning of the list in O(1) time. Delete (by value) The Deletenode() method can delete a node if its position is known (O(1) for the head) or requires O(n) to find the node first. Search The search() method iterates through the list, resulting in O(n) time complexity.
In HashMap(or Object/Map), The set() method adds a new key-value pair to the hashmap in O(1) time on average. In The get() method retrieves the value associated with a given key in O(1) time on average. Also, The delete() method removes a key-value pair from the hashmap in O(1) time on average. While the average time complexities for insert, delete, and search operations are O(1), it's essential to note that in the worst case such as many collisions of data, these operations can degrade to O(n). However, good hash functions and resizing strategies can mitigate this.
In Set, in inserting, the add() method adds a new element to the set in O(1) time on average. Delete The delete() method removes an element from the set in O(1) time on average. and for search, the has() method checks for the presence of an element in the set in O(1) time on average.
In Tree, to Insert, The insert() method adds a new node to the tree. In balanced trees, it can be done in O(log n) time, but in an unbalanced tree, it could degrade to O(n) if the tree becomes a linear chain. In delete operations, The delete() method removes a node from the tree. Similar to insertion, deletion in balanced trees takes O(log n), while it may take O(n) in unbalanced trees. and search is using search() method looks for a value in the tree. In balanced trees, this is efficient at O(log n), while in unbalanced trees, it can take O(n). In analyzing performance, Balanced trees maintain a specific structure that ensures the height remains logarithmic relative to the number of nodes, ensuring efficient operations. Unbalanced trees can degrade to linear structures, leading to poor performance.
Example Code in TypeScript: Provide a TypeScript code snippet demonstrating how to use each data structure.
Array
import { Component } from '@angular/core';
@Component({
selector: 'app-array-example',
template: `<p>{{ numbers }}</p>`
})
export class ArrayExampleComponent {
numbers: number[] = [1, 2, 3, 4, 5];
constructor() {
this.numbers.push(6); // Add an element to the array
}
}
Arrays in TypeScript are used to store multiple values in a single variable. In this example, we define an array called numbers that initially contains five integers. The push method is used to add another element, 6, to the end of the array. This allows dynamic manipulation of the list of numbers.
Tuple
import { Component } from '@angular/core';
@Component({
selector: 'app-tuple-example',
template: `<p>{{ person }}</p>`
})
export class TupleExampleComponent {
person: [number, string] = [1, "John"];
}
Tuples are a special type of array where each element can be of a different type. In this case, we create a tuple called
person
that contains a number (an ID) and a string (a name). This is useful for representing fixed collections of related values, maintaining their types within the same variable.ArrayList (Dynamic Arrays)
import { Component } from '@angular/core';
@Component({
selector: 'app-arraylist-example',
template: `<p>{{ dynamicArray }}</p>`
})
export class ArrayListExampleComponent {
dynamicArray: number[] = [];
constructor() {
this.dynamicArray.push(1); // Adding elements dynamically
this.dynamicArray.push(2);
}
}
Dynamic arrays, or ArrayLists, allow for the addition and removal of elements at runtime. In this example, we initialize an empty array dynamicArray and use the push method to add two elements, 1 and 2. This structure is useful for cases where the number of elements is not known in advance.
Stack
import { Component } from '@angular/core';
@Component({
selector: 'app-stack-example',
template: `<p>{{ poppedItem }}</p>`
})
export class StackExampleComponent {
private items: number[] = [];
poppedItem: number | undefined;
push(item: number) {
this.items.push(item); // Add an item to the stack
}
pop() {
return this.items.pop(); // Remove and return the top item
}
constructor() {
this.push(1); // Pushing items onto the stack
this.push(2);
this.poppedItem = this.pop(); // Pops the last item added
}
}
Stacks are based on the LIFO (Last In First Out) principle, meaning the last item added is the first one to be removed. In this example, we define a stack using an array and provide push and pop methods. We add two numbers and then remove the last one added. This structure is useful for scenarios like managing function calls or undo operations.
- Queue
import { Component } from '@angular/core';
@Component({
selector: 'app-queue-example',
template: `<p>{{ dequeuedItem }}</p>`
})
export class QueueExampleComponent {
private items: number[] = [];
dequeuedItem: number | undefined;
enqueue(item: number) {
this.items.push(item); // Add an item to the queue
}
dequeue() {
return this.items.shift(); // Remove and return the first item
}
constructor() {
this.enqueue(1); // Adding items to the queue
this.enqueue(2);
this.dequeuedItem = this.dequeue(); // Dequeues the first item added
}
}
Queues operate on a FIFO (First In First Out) basis, where the first item added is the first one to be removed. In this example, we implement a simple queue using an array with enqueue and dequeue methods. This is often used in scenarios such as scheduling tasks or managing requests in applications.
- LinkedList
import { Component } from '@angular/core';
class Node {
value: number;
next: Node | null = null;
constructor(value: number) {
this.value = value; // Initialize node with a value
}
}
@Component({
selector: 'app-linkedlist-example',
template: `<p>{{ head?.value }}</p>`
})
export class LinkedListExampleComponent {
head: Node | null = null;
add(value: number) {
const newNode = new Node(value); // Create a new node
if (!this.head) {
this.head = newNode; // If list is empty, set head
} else {
let current = this.head;
while (current.next) {
current = current.next; // Traverse to the end of the list
}
current.next = newNode; // Add new node at the end
}
}
constructor() {
this.add(1); // Adding nodes to the linked list
this.add(2);
}
}
Linked lists consist of nodes that contain data and a reference to the next node. This structure allows for dynamic memory allocation and efficient insertions/deletions. In this example, we create a linked list by defining a Node class and a component that manages the list. Nodes are added to the end of the list, demonstrating how you can build a dynamic data structure.
- HashMap (Object/Map)
import { Component } from '@angular/core';
@Component({
selector: 'app-hashmap-example',
template: `<p>{{ map.get('one') }}</p>`
})
export class HashMapExampleComponent {
map: Map<string, number> = new Map(); // Initializing a Map
constructor() {
this.map.set("one", 1); // Adding key-value pairs
this.map.set("two", 2);
}
}
HashMaps, or Maps, allow storage of key-value pairs for quick retrieval based on keys. In this example, we use the Map class to create a hashmap that associates strings (keys) with numbers (values). This structure is very useful for situations where you need to access elements by unique identifiers efficiently.
- Set
import { Component } from '@angular/core';
@Component({
selector: 'app-set-example',
template: `<p>{{ set.size }}</p>`
})
export class SetExampleComponent {
set: Set<number> = new Set(); // Initializing a Set
constructor() {
this.set.add(1); // Adding elements to the set
this.set.add(2);
this.set.add(1); // Duplicates are ignored
}
}
Sets are collections of unique values. They automatically ignore duplicate entries, ensuring all values are distinct. In this example, we initialize a Set and add numbers to it. The size property shows how many unique elements are in the set, demonstrating its usefulness for tracking unique items.
- Tree
import { Component } from '@angular/core';
class TreeNode {
value: number;
children: TreeNode[] = []; // List of child nodes
constructor(value: number) {
this.value = value; // Initialize node with a value
}
addChild(node: TreeNode) {
this.children.push(node); // Add a child node
}
}
@Component({
selector: 'app-tree-example',
template: `<p>{{ root.value }}</p>`
})
export class TreeExampleComponent {
root = new TreeNode(1); // Creating the root of the tree
constructor() {
const child = new TreeNode(2); // Creating a child node
this.root.addChild(child); // Adding child to the root
}
}
Trees are hierarchical structures composed of nodes, with each node potentially having multiple children. This structure allows for efficient data representation and retrieval. In this example, we define a Treenode class and build a simple tree with a root and a child. Trees are widely used in scenarios like organizing hierarchical data example are file systems.
- Data Structures to Cover:
Arrays:
Arrays in TypeScript are a fundamental data structure that allows you to store a collection of items. They can hold elements of any type, including primitives (like numbers and strings) and objects. In Angular, arrays are commonly used for handling lists of data, managing state, and facilitating user interactions.
- Provide examples of operations like adding, removing, and accessing elements.
import { Component } from '@angular/core';
@Component({
selector: 'app-fruit-list',
template: `appfruitlist.component.html`
})
export class FruitListComponent {
fruits: string[] = ['apple', 'banana', 'cherry'];
indexToAccess: number | undefined; // To hold the user input index
accessedFruit: string | undefined; // To hold the accessed fruit
addFruit(fruit: string) {
this.fruits.push(fruit);
}
removeFruit() {
this.fruits.pop();
}
accessFruit() {
// Check if the index is valid
if (this.indexToAccess !== undefined && this.indexToAccess >= 0 && this.indexToAccess < this.fruits.length) {
this.accessedFruit = this.fruits[this.indexToAccess];
} else {
this.accessedFruit = undefined; // Reset if index is invalid
}
}
}
<h2>Fruit List</h2>
<ul>
<li *ngFor="let fruit of fruits">{{ fruit }}</li>
</ul>
<button (click)="addFruit('orange')">Add Orange</button>
<button (click)="removeFruit()">Remove Last Fruit</button>
<div>
<input type="number" [(ngModel)]="indexToAccess" placeholder="Enter index to access" />
<button (click)="accessFruit()">Access Fruit</button>
<p *ngIf="accessedFruit !== undefined">Accessed Fruit: {{ accessedFruit }}</p>
</div>
Assumption this is example of creating an array in typescript in angular
Using push method
addFruit(fruit: string) {
this.fruits.push(fruit);
}
This method takes a string parameter fruit and adds it to the fruits array using the push() method. It dynamically updates the list of fruits displayed in the template.
Remove or delete the last fruit,
removeFruit() {
this.fruits.pop();
}
This method removes the last fruit from the fruits array using the pop() method. This operation also updates the displayed list of fruits.
Accessing Elements,
accessFruit() {
// Check if the index is valid
if (this.indexToAccess !== undefined && this.indexToAccess >= 0 && this.indexToAccess < this.fruits.length) {
this.accessedFruit = this.fruits[this.indexToAccess];
} else {
this.accessedFruit = undefined; // Clear when the indices are invalid input
}
}
This method checks whether the user-provided indextoAccess is valid. It ensures indextoAccess is not undefined, is greater than or equal to 0, and is less than the length of the fruits array. If valid, it assigns the corresponding fruit from the fruits array to the accessedfruit property. If invalid, it resets accessedfruit to undefined, which means no fruit is accessed.
Tuple:
Tuples are a special type of array that allows you to express an array with a fixed number of elements whose types are known. Unlike regular arrays, where elements can be of any type and can change in number, tuples have a defined structure where each position in the tuple has a specific type.
- Provide an example of defining and accessing tuple elements.
import { Component } from '@angular/core';
@Component({
selector: 'app-user-info',
templateUrl: './user-info.component.html',
styleUrls: ['./user-info.component.css']
})
export class UserInfoComponent {
// Defining a tuple with a string and a number
user: [string, number] = ['Alice', 30];
// Accessing tuple elements
name: string = this.user[0]; // Accessing the name
age: number = this.user[1]; // Accessing the age
// Method to return user info
getUserInfo(): string {
return `Name: ${this.name}, Age: ${this.age}`;
}
}
In code snippets, this is how defining and accessing the elements using tuples.
// Defining a tuple with a string and a number
user: [string, number] = ['Alice', 30];
This line defines a tuple named user in TypeScript, which is a fixed-size array that contains two elements: a string (the name "Alice") and a number (the age 30). The tuple ensures that the first element is always a string and the second element is always a number.
// Accessing tuple elements
name: string = this.user[0]; // Accessing the name
age: number = this.user[1]; // Accessing the age
In this accessing, using name: string = this.user[0]; retrieves the first element of the user tuple, which is the name ("Alice"), and assigns it to the variable name, specifying that it should be of type string. Also, age: number = this.user[1]; retrieves the second element of the user tuple, which is the age (30), and assigns it to the variable age, specifying that it should be of type number.
// Method to return user info
getUserInfo(): string {
return Name: ${this.name}, Age: ${this.age};
}
getUserInfo(): string specifies that this method returns a value of type string. Inside the method, the return statement constructs and returns a formatted string that includes the user's name and age, using template literals. This interpolates the name and age variables, resulting in a string like "Name: Alice, Age: 30."
ArrayList (Dynamic Arrays):
Dynamic arrays can be created using the built-in Array type. Unlike static arrays, which have a fixed size, dynamic arrays can grow or shrink as needed. You can manage dynamic arrays by adding, removing, or modifying elements using various array methods.
- Provide an example of resizing or manipulating dynamic arrays.
import { Component } from '@angular/core';
@Component({
selector: 'app-dynamic-array',
templateUrl: './dynamic-array.component.html',
styleUrls: ['./dynamic-array.component.css']
})
export class DynamicArrayComponent {
// Initialize a dynamic array
numbers: number[] = [];
// Method to add a number to the array
addNumber(num: number): void {
this.numbers.push(num); // Adds the number to the end of the array
}
// Method to remove the last number from the array
removeLastNumber(): void {
this.numbers.pop(); // Removes the last element
}
// Method to clear the entire array
clearArray(): void {
this.numbers = []; // Resets the array to empty
}
}
This is the typescript in angular
<div>
<h2>Dynamic Array Management</h2>
<input #numberInput type="number" placeholder="Enter a number" />
<button (click)="addNumber(numberInput.valueAsNumber)">Add Number</button>
<button (click)="removeLastNumber()">Remove Last Number</button>
<button (click)="clearArray()">Clear Array</button>
<h3>Current Numbers:</h3>
<ul>
<li *ngFor="let num of numbers">{{ num }}</li>
</ul>
</div>
This is the code snippets in html connected to our typescript. In this explanation
A dynamic array number is initialized to hold numeric values.
The addNumber method adds a number to the array using push().
The removeLastNumber method removes the last element using pop().
The clearArray method resets the array to empty.
The template provides an interface to add, remove, and clear numbers from the dynamic array.
Stack:
Define how to implement a stack in TypeScript using an array or a class.
Explain the Last In First Out (LIFO) principle and common operations (push, pop, peek).
Queue:
A stack is a data structure that follows the Last In First Out (LIFO) principle, meaning that the last element added to the stack is the first one to be removed. You can think of it like a stack of plates, tama? the last plate placed on top is the first one you take off.
Explain the First in First Out (FIFO) principle and common operations (enqueue, dequeue)
import { Component } from '@angular/core'; class Stack<T> { private items: T[] = []; // Array to hold stack elements // Push: Add an element to the top of the stack push(item: T): void { this.items.push(item); } // Pop: Remove the top element from the stack and return it pop(): T | undefined { return this.items.pop(); } // Peek: Return the top element without removing it peek(): T | undefined { return this.items[this.items.length - 1]; } // Check if the stack is empty isEmpty(): boolean { return this.items.length === 0; } // Get the size of the stack size(): number { return this.items.length; } // Get all items in the stack (for display purposes) getItems(): T[] { return this.items; } } @Component({ selector: 'app-stack-example', templateUrl: './stack-example.component.html', styleUrls: ['./stack-example.component.css'] }) export class StackExampleComponent { stack: Stack<number> = new Stack<number>(); // Create a new stack instance inputNumber: number | null = null; // Variable to hold user input poppedValue: number | undefined; // To store popped value peekedValue: number | undefined; // To store peeked value // Method to push a number onto the stack addNumber(): void { if (this.inputNumber !== null) { this.stack.push(this.inputNumber); this.inputNumber = null; // Clear input after pushing } } // Method to pop a number from the stack removeNumber(): void { this.poppedValue = this.stack.pop(); } // Method to peek at the top number of the stack checkTop(): void { this.peekedValue = this.stack.peek(); } // Get current stack items for display getCurrentStack(): number[] { return this.stack.getItems(); } }
<div>
<h2>Stack Example</h2>
<input type="number" [(ngModel)]="inputNumber" placeholder="Enter a number" />
<button (click)="addNumber()">Push to Stack</button>
<button (click)="removeNumber()">Pop from Stack</button>
<button (click)="checkTop()">Peek at Top</button>
<h3>Current Stack:</h3>
<ul>
<li *ngFor="let num of getCurrentStack()">{{ num }}</li>
</ul>
<h3>Peeked Value: {{ peekedValue }}</h3>
<h3>Popped Value: {{ poppedValue }}</h3>
</div>
In this explanation of each, Using push(item: T): void, This method adds a new element (item
) to the top of the stack.
Parameters:
item
: The element to be added, which is of typeT
(generic type).
Returns: Nothing (void).
pop(): T | undefined
Description: This method removes and returns the top element of the stack.
Returns: The removed element of type
T
orundefined
if the stack is empty.
peek(): T | undefined
Description: This method returns the top element of the stack without removing it.
Returns: The top element of type
T
orundefined
if the stack is empty.
isEmpty(): boolean
Description: This method checks whether the stack is empty.
Returns:
true
if the stack has no elements; otherwise,false
.
size(): number
Description: This method returns the current number of elements in the stack.
Returns: The size of the stack as a number.
getItems(): T[]
Description: This method returns all elements currently in the stack.
Returns: An array of elements of type
T
.
StackExampleComponent Methods
addNumber(): void
Description: This method takes the user-input number and pushes it onto the stack.
Functionality:
Checks if
inputNumber
is notnull
.Calls the
push
method of theStack
instance to add the number.Resets
inputNumber
tonull
after pushing to clear the input field.
Returns: Nothing (void).
removeNumber(): void
Description: This method removes the top element from the stack and stores the removed value.
Functionality:
- Calls the
pop
method of theStack
instance and assigns the returned value topoppedValue
.
- Calls the
Returns: Nothing (void).
checkTop(): void
Description: This method checks the top element of the stack without removing it and stores the peeked value.
Functionality:
- Calls the
peek
method of theStack
instance and assigns the returned value topeekedValue
.
- Calls the
Returns: Nothing (void).
getCurrentStack(): number[]
Description: This method returns the current elements in the stack for display purposes.
Returns: An array of numbers that are currently in the stack, retrieved using the
getItems
method of theStack
instance.
LinkedList:
Creating a linked list in TypeScript involves defining a Node class to represent each node and a LinkedList class to manage the nodes. Here's how you can create both singly and doubly linked lists, along with examples of adding, removing, and traversing nodes.
- Provide an example of adding, removing, and traversing nodes.
Linked List Implementation
class Node<T> {
value: T;
next: Node<T> | null;
constructor(value: T) {
this.value = value;
this.next = null;
}
}
class LinkedList<T> {
head: Node<T> | null;
constructor() {
this.head = null;
}
// Add a node to the end of the list
add(value: T): void {
const newNode = new Node(value);
if (!this.head) {
this.head = newNode;
return;
}
let current = this.head;
while (current.next) {
current = current.next;
}
current.next = newNode;
}
// Remove a node by value
remove(value: T): void {
if (!this.head) return;
// If the head needs to be removed
if (this.head.value === value) {
this.head = this.head.next;
return;
}
let current = this.head;
while (current.next) {
if (current.next.value === value) {
current.next = current.next.next;
return;
}
current = current.next;
}
}
// Traverse the list and print values
traverse(): void {
let current = this.head;
while (current) {
console.log(current.value);
current = current.next;
}
}
}
// Example usage
const linkedList = new LinkedList<number>();
linkedList.add(1);
linkedList.add(2);
linkedList.add(3);
console.log('Linked List after adding nodes:');
linkedList.traverse();
linkedList.remove(2);
console.log('Linked List after removing node with value 2:');
linkedList.traverse();
Explanation
Node Class: Represents a single node in the linked list, containing a value and a pointer to the next node.
LinkedList Class: Manages the linked list with methods to add, remove, and traverse nodes.
add(value: T)
: Adds a new node with the specified value to the end of the list.remove(value: T)
: Removes the first node that matches the specified value.traverse()
: Logs the values of all nodes in the list.
Integration into an Angular Component
To integrate this into an Angular component, you could create a component and include the linked list functionality in it. Here’s a basic setup:
import { Component } from '@angular/core';
@Component({
selector: 'app-linked-list',
template: `<h1>Linked List Example</h1>`,
})
export class LinkedListComponent {
// Include the LinkedList implementation here
constructor() {
const linkedList = new LinkedList<number>();
linkedList.add(1);
linkedList.add(2);
linkedList.add(3);
console.log('Linked List after adding nodes:');
linkedList.traverse();
linkedList.remove(2);
console.log('Linked List after removing node with value 2:');
linkedList.traverse();
}
}
Usage
- You can use this LinkedListComponent in your Angular application. When the component is initialized, it will demonstrate adding and removing nodes, as well as traversing the list. Adjust the LinkedList class as needed to suit your requirements!
HashMap (or Object/Map):
In TypeScript, you can use either Map
or plain objects to create a key-value pair data structure. Below, I'll demonstrate both approaches along with examples for inserting, deleting, and searching for values by keys.
Provide examples of inserting, deleting, and searching for values by keys.
Using
Map
The
Map
object is a collection of key-value pairs where keys can be of any type, including objects.Creating and Using a Map
// Creating a Map const mapExample = new Map<string, number>(); // Inserting key-value pairs mapExample.set('one', 1); mapExample.set('two', 2); mapExample.set('three', 3); console.log('Map after inserting values:'); console.log(mapExample); // Searching for a value by key const valueTwo = mapExample.get('two'); console.log('Value for key "two":', valueTwo); // Deleting a key-value pair mapExample.delete('two'); console.log('Map after deleting key "two":'); console.log(mapExample); // Checking if a key exists const hasTwo = mapExample.has('two'); console.log('Does key "two" exist?', hasTwo);
OUTPUT
Map after inserting values: Map(3) { 'one' => 1, 'two' => 2, 'three' => 3 } Value for key "two": 2 Map after deleting key "two": Map(2) { 'one' => 1, 'three' => 3 } Does key "two" exist? false
Using Plain Object
You can also use a plain object to create a key-value structure, where keys are strings (or symbols), and values can be any type.
Creating and Using an Object
// Creating an object
const objExample: { [key: string]: number } = {};
// Inserting key-value pairs
objExample['one'] = 1;
objExample['two'] = 2;
objExample['three'] = 3;
console.log('Object after inserting values:');
console.log(objExample);
// Searching for a value by key
const valueTwoObj = objExample['two'];
console.log('Value for key "two":', valueTwoObj);
// Deleting a key-value pair
delete objExample['two'];
console.log('Object after deleting key "two":');
console.log(objExample);
// Checking if a key exists
const hasTwoObj = 'two' in objExample;
console.log('Does key "two" exist?', hasTwoObj);
Output
Object after inserting values:
{ one: 1, two: 2, three: 3 }
Value for key "two": 2
Object after deleting key "two":
{ one: 1, three: 3 }
Does key "two" exist? false
Summary
Map:
Allows keys of any type.
Maintains insertion order.
Provides built-in methods like
set
,get
,delete
, andhas
.
Object:
Only allows string or symbol keys.
Keys are unordered.
Accessing keys is done via bracket notation or dot notation.
You can choose either method based on your specific use case and requirements. For complex key types or if you need to maintain order, Map
is usually the better option. For simpler use cases, a plain object may suffice.
SET:
To create a Set
in Angular (or JavaScript) that stores unique elements, you can use the Set
constructor. By instantiating a Set
, you establish a collection that only allows distinct values, meaning any duplicates will not be added. For example, you can create an empty Set
with const mySet = new Set<number>()
, where number
indicates the type of elements it will hold. Additionally, you can initialize a Set
with existing values by passing an array to the constructor, such as const mySetWithValues = new Set<number>([1, 2, 3])
, which adds the numbers 1, 2, and 3 to the set while ensuring uniqueness.
- Show how to add, remove, and check for elements in a set
1. Adding Elements in Angular
To add elements to a Set
in Angular, you use the add
method. This method allows you to insert a value into the Set
, and it ensures that each value is unique duplicates will not be added.
import { Component } from '@angular/core';
@Component({
selector: 'app-set-example',
template: `<button (click)="addElement(1)">Add 1</button>
<button (click)="addElement(2)">Add 2</button>
<p>Current Set: {{ currentSet }}</p>`,
})
export class SetExampleComponent {
mySet: Set<number> = new Set<number>();
currentSet: string = '';
addElement(value: number): void {
this.mySet.add(value);
this.updateCurrentSet();
}
private updateCurrentSet(): void {
this.currentSet = Array.from(this.mySet).join(', ');
}
}
In this Angular component, mySet
is initialized as an empty Set
that stores numbers. The addElement
method is called when the "Add" buttons are clicked, adding the specified number to mySet
using the add
method. The updateCurrentSet
method is called to update the display of the current set, converting the set to an array and joining the elements into a string format.
Removing Elements in Angular
To remove elements from a
Set
, you can use thedelete
method. This method takes a value as an argument and removes it from the set if it exists.
<button (click)="removeElement(1)">Remove 1</button>
<button (click)="removeElement(2)">Remove 2</button>
removeElement(value: number): void {
this.mySet.delete(value);
this.updateCurrentSet();
}
In this part of the Angular component, two buttons allow the user to remove values from mySet
. The removeElement
method uses the delete
method to attempt to remove the specified number. After removing (or attempting to remove) the element, updateCurrentSet
is called to refresh the displayed set content.
Checking for Elements in Angular
To check if an element exists in a
Set
, you use thehas
method. This method returnstrue
if the value is found andfalse
if it is not.
<button (click)="checkElement(1)">Check 1</button>
<button (click)="checkElement(2)">Check 2</button>
checkElement(value: number): void {
const exists = this.mySet.has(value);
alert(`Does ${value} exist in the set? ${exists}`);
}
Here, buttons trigger the checkElement
method, which checks for the existence of specified numbers in mySet
. The has
method is used to determine whether the number is present, and an alert displays the result to the user. This allows users to quickly verify if specific values exist in the set.
import { Component } from '@angular/core';
@Component({
selector: 'app-set-example',
template: `
<button (click)="addElement(1)">Add 1</button>
<button (click)="addElement(2)">Add 2</button>
<button (click)="removeElement(1)">Remove 1</button>
<button (click)="removeElement(2)">Remove 2</button>
<button (click)="checkElement(1)">Check 1</button>
<button (click)="checkElement(2)">Check 2</button>
<p>Current Set: {{ currentSet }}</p>
`,
})
export class SetExampleComponent {
mySet: Set<number> = new Set<number>();
currentSet: string = '';
addElement(value: number): void {
this.mySet.add(value);
this.updateCurrentSet();
}
removeElement(value: number): void {
this.mySet.delete(value);
this.updateCurrentSet();
}
checkElement(value: number): void {
const exists = this.mySet.has(value);
alert(`Does ${value} exist in the set? ${exists}`);
}
private updateCurrentSet(): void {
this.currentSet = Array.from(this.mySet).join(', ');
}
}
Adding Elements: Use the
add
method to insert unique values into theSet
. Duplicates are ignored.Removing Elements: The
delete
method removes a specific value from theSet
, leaving it unchanged if the value is not found.Checking for Elements: The
has
method checks for the presence of a value in theSet
, returning a boolean indicating whether the value exists.
This example illustrates how to effectively manage unique elements in a Set
within an Angular components.
Tree:
Binary trees and binary search trees (BST) can be implemented in TypeScript using classes to define the tree structure and its operations. A binary tree consists of nodes, where each node has a maximum of two children, typically referred to as the left and right children. A binary search tree is a special kind of binary tree where the left child contains values less than the parent node, and the right child contains values greater than the parent node.
Implementing a Binary Search Tree (BST)
Here’s how you can implement a binary search tree in TypeScript, along with examples of inserting nodes, traversing the tree, and searching for elements.
Provide an example showing how to insert nodes, traverse the tree, and search for elements.
1. Node Class
First, define a Node
class to represent each node in the tree.
typescriptCopy codeclass Node {
value: number;
left: Node | null;
right: Node | null;
constructor(value: number) {
this.value = value;
this.left = null;
this.right = null;
}
}
Explanation: The Node
class has three properties: value
(the value stored in the node), left
(a reference to the left child node), and right
(a reference to the right child node). The left and right properties are initialized to null
by default.
2. Binary Search Tree Class
Next, create a BinarySearchTree
class that manages the nodes and operations.
typescriptCopy codeclass BinarySearchTree {
root: Node | null;
constructor() {
this.root = null;
}
// Insert a new value into the BST
insert(value: number): void {
const newNode = new Node(value);
if (this.root === null) {
this.root = newNode;
} else {
this.insertNode(this.root, newNode);
}
}
private insertNode(node: Node, newNode: Node): void {
if (newNode.value < node.value) {
if (node.left === null) {
node.left = newNode;
} else {
this.insertNode(node.left, newNode);
}
} else {
if (node.right === null) {
node.right = newNode;
} else {
this.insertNode(node.right, newNode);
}
}
}
}
Explanation: The BinarySearchTree
class has a root
property to store the root node of the tree. The insert
method is used to add a new value. If the root is null
, it assigns the new node as the root; otherwise, it calls the insertNode
method to find the correct position for the new node. The insertNode
method recursively places the new node in the left or right subtree based on its value.
3. Traversing the Tree
You can traverse the tree in various ways, such as in-order, pre-order, or post-order. Here’s an example of in-order traversal, which visits the left child, the current node, and then the right child.
typescriptCopy codeinOrderTraversal(node: Node | null): number[] {
const result: number[] = [];
if (node !== null) {
result.push(...this.inOrderTraversal(node.left));
result.push(node.value);
result.push(...this.inOrderTraversal(node.right));
}
return result;
}
Explanation: The inOrderTraversal
method recursively visits the nodes. It checks if the current node is not null
, then recursively traverses the left subtree, adds the current node's value to the result array, and finally traverses the right subtree. The method returns an array of values in sorted order.
4. Searching for Elements
To search for an element in the BST, use the following method:
typescriptCopy codesearch(value: number): boolean {
return this.searchNode(this.root, value);
}
private searchNode(node: Node | null, value: number): boolean {
if (node === null) {
return false; // Value not found
}
if (value === node.value) {
return true; // Value found
} else if (value < node.value) {
return this.searchNode(node.left, value); // Search left subtree
} else {
return this.searchNode(node.right, value); // Search right subtree
}
}
Explanation: The search
method initiates the search process by calling searchNode
. The searchNode
method checks if the current node is null
. If it is, the value is not found, so it returns false
. If the current node’s value matches the search value, it returns true
. If the search value is less than the current node’s value, it recursively searches the left subtree; otherwise, it searches the right subtree.
Complete Example
Here’s a complete implementation of a binary search tree in TypeScript:
typescriptCopy codeclass Node {
value: number;
left: Node | null;
right: Node | null;
constructor(value: number) {
this.value = value;
this.left = null;
this.right = null;
}
}
class BinarySearchTree {
root: Node | null;
constructor() {
this.root = null;
}
insert(value: number): void {
const newNode = new Node(value);
if (this.root === null) {
this.root = newNode;
} else {
this.insertNode(this.root, newNode);
}
}
private insertNode(node: Node, newNode: Node): void {
if (newNode.value < node.value) {
if (node.left === null) {
node.left = newNode;
} else {
this.insertNode(node.left, newNode);
}
} else {
if (node.right === null) {
node.right = newNode;
} else {
this.insertNode(node.right, newNode);
}
}
}
inOrderTraversal(node: Node | null): number[] {
const result: number[] = [];
if (node !== null) {
result.push(...this.inOrderTraversal(node.left));
result.push(node.value);
result.push(...this.inOrderTraversal(node.right));
}
return result;
}
search(value: number): boolean {
return this.searchNode(this.root, value);
}
private searchNode(node: Node | null, value: number): boolean {
if (node === null) {
return false; // Value not found
}
if (value === node.value) {
return true; // Value found
} else if (value < node.value) {
return this.searchNode(node.left, value); // Search left subtree
} else {
return this.searchNode(node.right, value); // Search right subtree
}
}
}
Summary
Node Class: Represents each node with a value and references to left and right children.
Binary Search Tree Class: Manages the nodes and provides methods for inserting values, traversing the tree, and searching for elements.
Insert Operation: Adds a new value to the correct position in the tree while maintaining the BST property.
Traversal Operation: The
inOrderTraversal
method retrieves values in sorted order.Search Operation: The
search
method checks if a specific value exists in the BST.
This implementation provides a basic understanding of how binary search trees work in TypeScript.
References:
Linked Lists in Typescript (Typescript Data Structures Part 1) (youtube.com)