Ashish's Wiki

This wiki is a curated collection of insights, best practices, and learnings from my journey in software engineering. It covers key concepts and code examples aimed at deepening technical understanding.

Links

[Website] [Notes] [Uses] [Todos] [Listens] [Movies] [Shows] [Books]

Topics

Main topics covered by this wiki:

Algorithms

  • Recursion
  • Sorting
  • Round Robin
  • Divide And Conquer
  • Backtracking
  • Greedy Algorithms
  • Dynamic Programming
  • Asymptotic Analysis

Asymptotic Analysis

A problem with complexity O(n) which takes 2^60 times will take 60 times in O(log n) complexity

<-- FastSlow -->
NameConstantLogarithmiclinearQuadraticexponential
NotationO(1)O(logn)O(n)O(n^2)O(k^n)
for(var i...){ // O(n)
	1+1; // O(1)
}
/**
* Complexity = O(n^2) + O(2) 
* We only care about worst case so we consider the worst time complexity only.
* i.e we take the highest order of the polynomial
* So the complexity is O(n^2)
*/
for(var i...){ // O(n)
	for(var j...){ // O(n)
		3+3; // O(1)
		5+6; // O(1)
	}
}

O(log n)

for(let i = 0; i < n; i*2){ //O(log n)
	stmt;
}

Backtracking

  1. Technique used to solve problems with a large search space, by systematically trying and eliminating possibilities.
  2. Hence applied to problems not solvable in polynomial time and which are generally solvable using exhaustive search.

We use backtracking for NP Complete problem (Hard problem). As hard problem requires exponential time to solve it systematically.

  • Each step tries to extend a partial solution by adding another element at the end.
  • Tests if a solution is obtained. If yes, output, count and continue
  • Otherwise checks, if it is extendible. If yes, continue.
  • Otherwise backtracks.

Steps

  1. Construct the state space tree
  2. Explore the state space tree using depth first search.
  3. Prune non promising nodes.

State space tree

  1. A tree of choices being made.
  2. Root represents an initial state.
  3. Nodes of the first level represent the choices made for the first component of the solution.
  4. Nodes of the second level represent the choices for the second component.
  5. Thus each node represents a partial solution.
  6. An edge from node x to y exists if y was created by advancing from x.

Nodes

  1. Node is promising if it corresponds to a partially constructed solution that may still lead to a complete solution.
  2. Otherwise it is called non-promising.
  3. Leaves represent either non-promising dead ends or complete solution found.

Examples

  1. Finding a hamiltonian circuit
  2. Finding the most valuable subset of items in a knapsack
  3. Going through a maze
  4. Solving the 8-Queens problem

Divide and Conquer

In Divide and Conquer, the problem is divided into smaller sub-problems and then each problem is solved independently. The solution of all sub problems is merged to get the solution of the origin problem.

Examples

  1. Merge Sort
  2. Quick Sort
  3. Binary Search
  4. Strassen's Matrix Multiplication
  5. Closest pair (points)

Dynamic Programming

In Dynamic programming we break up a problem into a series of overlapping subproblems and build up solutions to larger and larger subproblems.

Examples

  1. Fibonacci number series
  2. Knapsack problem
  3. Tower of Hanoi
  4. All pair shortest path by Floyd Warshall
  5. Shortest path by Dijkstra
  6. Project scheduling

Greedy Algorithms

Introduction

In greedy algorithms, we find the optimum solution. The closest solution which may be optimum is chosen. It may lead to optimized solutions but mostly greedy algorithms do not provide globally optimized solutions.

Examples

  1. Travelling salesman problem

  2. Prim's Minimal Spanning Tree Algorithm

  3. Kruskal's Minimal Spanning Tree Algorithm

  4. Dijkstra's Minimal Spanning Tree Algorithm

  5. Graph - Map Coloring

  6. Graph - Vertex Cover

  7. Knapsack Problem

  8. Job Scheduling Problem

Recursion

The process in which a function calls itself directly or indirectly is called recursion.

Round Robin

The Round Robin scheduling algorithm is a simple and efficient method used in operating systems to manage processes. It allocates a fixed time slice or "quantum" to each process in the queue, cycling through them repeatedly.

How It Works:

  1. Processes Queue: All processes are placed in a queue.
  2. Time Quantum: Each process is given a fixed amount of time (quantum) to execute.
  3. Cycle Through: The scheduler cycles through the queue, giving each process its turn.
  4. Preemption: If a process doesn't finish within its time slice, it is moved to the back of the queue.

Example:

Suppose we have three processes, P1, P2, and P3, with a time quantum of 3 units.

  • Initial Queue: P1, P2, P3

  • Execution Order:

    1. P1 executes for 3 units, then moves to the back.
    2. P2 executes for 3 units, then moves to the back.
    3. P3 executes for 3 units, then moves to the back.
    4. Cycle Repeats: Each process gets another turn if they haven't finished.

Advantages:

  • Fairness: Each process gets an equal share of CPU time.
  • Simplicity: Easy to implement and understand.

Disadvantages:

  • Inefficiency: Time quantum needs to be carefully chosen; too small can lead to high overhead, too large can cause delays.

This approach ensures that all processes are treated equally and prevents any single process from monopolizing the CPU.

Sorting

Bubble Sort

It works by swapping the adjacent elements if they are in wrong order. It is the most inefficient sorting algorithm because of simple it is. It has the time complexity of O(n^2). Insertion sort is better than bubble sort because it has the same asymptotic complexity but only requires O(n) swaps whereas bubble sort require O(n2) swaps.

Psuedocode

let n be the length of the array
for i from 0 to n-1
	for j from 0 to n-1-i  //The last element is already sorted after each pass
		if arr[j] > arr[j+1]
			swap(arr[j], arr[j+1])

let the array be [3,2,4,1]

i=0

	j=0 //[3,2,4,1]
		3>2
		swap(3,2) //[2,3,4,1]

	j=1 //[2,3,4,1]
		3>4
	
	j=2 //[2,3,4,1]
		4>1
		swap(4,1) //[2,3,1,4]

i=1

	j=0 //[2,3,1,4]
		2>3

	j=1 //[2,3,1,4]
		3>1	
		swap(3,1) //[2,1,3,4]

i=2

	j=0 //[2,1,3,4]
		2>1
		swap(2,1) //[1,2,3,4]

Diagram

merge-sort

Optimization

we use a flag to track if there is swapping taking place, if there is no swapping it means the array is already sorted so no need for more pass.

for i from 0 to n-1
	swapped = 0
	for j from 0 to n-1-i  //The last element is already sorted after each pass
		if arr[j] > arr[j+1]
			swap(arr[j], arr[j+1])
			swapped = 1
	
	if swapped == 0 // No elements are swapped so stop the loop
		break

Insertion Sort

Insertion sort algorithm sorts an array by inserting an element from the unsorted part to correct position at sorted part.

Selection Sort

Selection sort algorithm sorts an array by repeatedly finding the minimum element from unsorted part and putting it at the beginning.

Psuedocode

let n be the length of the array
for i from 0 to n-1
	min = i
	for j from i+1 to n-1 
		if arr[j] < arr[min]
			min = j
	temp = arr[i]
	arr[i] = arr[min]
	arr[min] = temp
let the array be [3,2,4,1]

i=0
	min = 0
		j=1 //[3,2,4,1]
			2 < 3
				min = 1

		j=2	//[3,2,4,1]
			4 < 2
			
		j=3 //[3,2,4,1]
			1 < 2
				min = 3
	swap(a[i], a[min]) //[1,2,4,3]

i=1
	min = 1
		j=2 //[1,2,4,3]
			4 < 2

		j=3 //[1,2,4,3]
			3 < 2

i=2
	min = 2
		j = 3 //[1,2,4,3]
			3 < 4 
				
	swap(a[i], a[min]) //[1,2,3,4]




		



```<style>
  .scroll-to-top {
    font-size: 2.5rem;
    width: 3.2rem;
    height: 3.2rem;
    display: none;
    align-items: center;
    justify-content: center;
    position: fixed;
    padding: 0.75rem;
    bottom: 4rem;
    right: calc(1.25rem + 90px + var(--page-padding));
    z-index: 999;
    cursor: pointer;
    border: none;
    color: var(--bg);
    background: var(--fg);
    border-radius: 50%;
  }
  .scroll-to-top.hidden {
    display: none;
  }
  .scroll-to-top i {
    transform: translateY(-2px);
  }
  @media (min-width: 1080px) {
    .scroll-to-top {
      display: flex;
    }
  }
</style>
<button type="button" aria-label="scroll-to-top" class="scroll-to-top hidden" onclick="scrollToTop()">
  <i class="fa fa-angle-up"></i>
</button>
<script>
  const scrollToTop = () => window.scroll({ top: 0, behavior: "smooth" });
  window.addEventListener("scroll", () => {
    const button = document.querySelector(".scroll-to-top");
    button.classList.toggle("hidden", window.scrollY < 200);
  });
</script>
<style>
  footer {
    text-align: center;
    text-wrap: balance;
    margin-top: 5rem;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
  }
  footer p {
    margin: 0;
  }
</style>
<footer><p>Copyright © 2024 • Created with ❤️ by <a href="https://ashish.me">Ashish Patel</a></p>
</footer>

C Sharp

  • Oops
  • Generics
  • Linq
  • Design Patterns
  • Xunit
  • Entity
  • Delegates

Delegates

Delegates is an object that knows how to call a method or a group of methods.

Design Patterns

Design patterns may be said as a set of probable solutions for a particular problem which is tested to work best in certain situations.

Creational Patterns

These patterns deals mainly with creation of objects and classes.

Factory Method

Factory method design pattern abstract the process of object creation and allows the object to be created at run-time when it is required.

using System;

namespace FactoryPatternDemo
{
    // 'IProduct' Interface
    interface IFactory {
        void details();
    }

    // 'ConcreteProduct' class
    class PermanentEmployee : IFactory
    {
        public void details()
        {
            Console.WriteLine("This is permanent employee type object");
        }
    }
    // 'ConcreteProduct' class
    class TemporaryEmployee : IFactory
    {
        public void details()
        {
            Console.WriteLine("This is Temporary employee type object");
        }
    }
    // 'Creator' abstract class
    abstract class EmployeeFactory
    {
        public abstract IFactory Factory(string employeeType);
    }
    // 'ConcrteCreator' class
    class ConcreteEmployeeFactory : EmployeeFactory
    {
        public override IFactory Factory(string employeeType)
        {
            switch (employeeType)
            {
                case "PermanentEmployee":
                    return new PermanentEmployee();
                case "TemporaryEmployee":
                    return new TemporaryEmployee();
                default:
                    throw new ApplicationException(string.Format("This type of employee can not be created"));
            }
        }
    }

    // factory method design pattern demo
    // calling class/ client
    class Program
    {
        static void Main(string[] args)
        {
            EmployeeFactory EmployeeFactory = new ConcreteEmployeeFactory();

            IFactory permanentEmployee = EmployeeFactory.Factory("PermanentEmployee");
            permanentEmployee.details();

            IFactory TemporaryEmployee = EmployeeFactory.Factory("TemporaryEmployee");
            TemporaryEmployee.details();

            Console.ReadLine();
        }
    }
}

Singleton Method

Structural Patterns

These patterns deals with Class and Object Composition.

Behavioural Patterns

These mainly deals with Class - Object communication.

Entity

Entity Framework is an open-source ORM framework for .NET applications supported by Microsoft. It enables developers to work with data using objects of domain specific classes without focusing on the underlying database tables and columns where this data is stored.

Generics

Generics allow for designing a classes and methods whose types are specified only at the time of declaration and instantiation.

Without generics we will have to create class for every datatype. We can use object but it will involve boxing and unboxing. With generics, there is no need for that.

Advantages of Generics

  1. Casting is not necessary
  2. Generics makes code type safe
  3. Code is not duplicated for multiple types of data
using System;
using System.Collections;
using System.Collections.Generic;

public class Employee {
    public string Name {get; set;}
    public int EmployeeId {get;set;}
}

public class EmployeeList {
    public List<Employee> employeeList = new List<Employee>();
    public void addEmployee(Employee employee){
        this.employeeList.Add(employee);
    }
    public void print(){
		foreach (Employee item in employeeList) {
			Console.WriteLine(item.Name);
		}
    }
}

public class Program {
    public static void Main(string[] args){
        Employee emp1 = new Employee();
        emp1.Name = "Ashish Patel";
        emp1.EmployeeId = 189;
		Employee emp2 = new Employee();
        emp2.Name = "Ansu Patel";
        emp2.EmployeeId = 182;
        EmployeeList empList = new EmployeeList();
        empList.addEmployee(emp1);
		empList.addEmployee(emp2);
        empList.display();
    }
}

With generics we can simply write the above code as below

using System;
using System.Collections;
using System.Collections.Generic;

public class Employee {
    public string Name {get; set;}
    public int EmployeeId {get;set;}
}

public class EmployeeList<T> {
    public List<T> employeeList = new List<T>();
    public void addEmployee(T employee){
        this.employeeList.Add(employee);
    }
    public void display(){
		foreach (T item in employeeList) {
			Console.WriteLine(item.dump());
		}
    }
}

public class Program {
    public static void Main(string[] args){
        Employee emp1 = new Employee();
        emp1.Name = "Ashish Patel";
        emp1.EmployeeId = 189;
		Employee emp2 = new Employee();
        emp2.Name = "Ansu Patel";
        emp2.EmployeeId = 182;
        EmployeeList<Employee> empList = new EmployeeList<Employee>();
        empList.addEmployee(emp1);
		empList.addEmployee(emp2);
        empList.display();
    }
}

Linq

LINQ (Language Integrated Query) is uniform query syntax in C# and VB.NET used to save and retrieve data from different sources.

Types

It can be used in two ways i.e Query Syntax and Method Syntax.

Query Syntax

var result = from s in stringList
            where s.Contains("Ashish") 
            select s;

Method Syntax

var result = stringList.Where(s => s.Contains("Ashish"));

Query Operators

Standard Query Operators in LINQ are actually extension methods for the IEnumerable and IQueryable types.

Query Syntax

var result = from s in stringList
            where s.Contains("Ashish") 
            select s;
```<style>
  .scroll-to-top {
    font-size: 2.5rem;
    width: 3.2rem;
    height: 3.2rem;
    display: none;
    align-items: center;
    justify-content: center;
    position: fixed;
    padding: 0.75rem;
    bottom: 4rem;
    right: calc(1.25rem + 90px + var(--page-padding));
    z-index: 999;
    cursor: pointer;
    border: none;
    color: var(--bg);
    background: var(--fg);
    border-radius: 50%;
  }
  .scroll-to-top.hidden {
    display: none;
  }
  .scroll-to-top i {
    transform: translateY(-2px);
  }
  @media (min-width: 1080px) {
    .scroll-to-top {
      display: flex;
    }
  }
</style>
<button type="button" aria-label="scroll-to-top" class="scroll-to-top hidden" onclick="scrollToTop()">
  <i class="fa fa-angle-up"></i>
</button>
<script>
  const scrollToTop = () => window.scroll({ top: 0, behavior: "smooth" });
  window.addEventListener("scroll", () => {
    const button = document.querySelector(".scroll-to-top");
    button.classList.toggle("hidden", window.scrollY < 200);
  });
</script>
<style>
  footer {
    text-align: center;
    text-wrap: balance;
    margin-top: 5rem;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
  }
  footer p {
    margin: 0;
  }
</style>
<footer><p>Copyright © 2024 • Created with ❤️ by <a href="https://ashish.me">Ashish Patel</a></p>
</footer>

OOPS

Class

Class is the blueprint of the object as it stores data and functions. It does not occupy space as it is just the logical representation of data.

Object

Object is instance of class. When an object is created using new operator, memory is allocated for the class in the heap.

Abstraction

Astraction is to represent the essential feature without representing the background details. It lets you focus on what object does instead of how it does it. It is nothing but putting all the variables and methods in a class that are necessary.

abstract class Human {
	public void walking();
	public void talking();
}

public class Male: Human{
	public void Fighting();
}

public class Female: Human{
	public void dancing();
}

Encapsulation

Encapsulation is a technique used to protect the information in an object from another object. For example we can hide the data for security by making variable private and we expose the variable by making it public.

Inheritance

Inheritance is when a class includes property of another class.

Polymorphism

Polymorphism means when a function behaves in different forms depending upon the parameters.

xUnit

xUnit Elements

  • Fact - always true
  • Theory - true with right data
  • InlineData, MemberData, ClassData - passing data to a unit test

Clean Code

  • Conventional Commits

Conventional Commits

Format

<type>(<scope>): <description>

  • feat: A new feature
  • fix: A bug fix
  • docs: Documentation changes
  • style: Code style changes (formatting, etc.)
  • refactor: Code refactoring
  • test: Adding or modifying tests
  • chore: Routine tasks, maintenance, etc

Examples

  • fix(login): correct password hashing issue
  • fix(cart): prevent items from duplicating on refresh
  • fix(profile): resolve avatar upload error
  • fix(navbar): align links correctly on mobile
  • fix(modal): close button not responsive
  • fix(button): make hover effect consistent
  • fix(api): handle missing user ID in requests
  • fix(api): correct error codes for unauthorized access
  • fix(api): prevent null values in response
  • fix(db): ensure indexes are used for faster queries
  • fix(db): correct foreign key constraint in orders
  • fix(db): handle data migration for older records
  • fix(auth): resolve token expiry handling
  • fix(payment): correct tax calculation logic
  • fix(file-upload): prevent large files from causing timeout
  • fix(css): adjust padding for form inputs
  • fix(js): prevent null pointer error on page load
  • fix(svg): correct icon alignment in header
  • fix(store): ensure cart state persists on refresh
  • fix(state): prevent duplicate entries in wishlist
  • fix(redux): correct initial state for auth

Data Structures

  • Tree
  • Linked List
  • Queue
  • Array
  • Hash Table
  • Stack
  • Time Complexity
  • Graph

Array

Inserting a new item is quite slow // O(N)

Searching is quite fast with binary search // O(logN)

removing an item is slow //O(N)

Graph

Hash Table

Linked List

Inserting a new item is very fast //O(1)

Searching is sequential //O(N)

Removing an item is fast because of the references

Queue

Stack

Time Complexity

List of common complexities

In computer science, the performance of program is determined by total time and space taken to execute the program with respect to input.

Commonly used asymptotic notations for time and space complexity are below

  1. Omega notation (Best Case)
  2. Theta notation (Average Case)
  3. Oh notation (Worst Case)

We mostly consider Oh notation because it will give the execution time in the worst case.

Common Big O's

Let say if the input is N = 10 then the time taken by common asymptotic notations can be viewed from below table.

NameNotationTime
ConstantO(1)1
LogarithmicO(log N)3.3219
LinearO(N)10
LinearithmicO(N log N)33.219
PolynomialO(N^2)100
ExponentialO(2^N)1024
FactorialO(N!)3628800

Big O Graph

O(n) Linear Time

An algorithm is said to run in linear time if its time execution is directly proportional to the input size, i.e. time grows linearly as input size increases.

Consider the following examples, below I am linearly searching for an element, this has a time complexity of O(n).

int find = 66;
var numbers = new int[] { 33, 435, 36, 37, 43, 45, 66, 656, 2232 };
for (int i = 0; i < numbers.Length - 1; i++)
{
    if(find == numbers[i])
    {
        return;
    }
}

O(log n) Logarithmic Time:

An algorithm is said to run in logarithmic time if its time execution is proportional to the logarithm of the input size. Binary search and all the operations of binary search tree have logarithmic time complexity has we discard half of the data on every iteration.

O(n2) Quadratic Time

An algorithm is said to run in quadratic time if its time execution is proportional to the square of the input size.

Tree

Binary Search Tree

Binary search tree is a node based binary tree data structure.

Introduction

  • The left subtree of a node contains only nodes with keys less than the node's key.
  • The right subtree of a node contains only nodes with keys greater than the node's key.
  • If we goto the left as far as possible we will find the smalled node and if goto the right as far as possible than we will find the largest node.

Time Complexity : O(log N)

Important Points

  1. It keeps the keys in sorted order: so that lookup and other other operations can use the principle of binary search.
  2. Each comparison allows the operations to skip over half of the tree, so that each lookup/insertion/deletion takes time proportional to the logarithm of the number of items stored in the tree.
  3. This is much better than linear time O(N) required to find items by key in an unsorted array, but slower than the corresponding operations on hash tables.

Binary search tree have to be balanced to be efficient. Tree is balanced if the left subtree contains as many nodes as the right subtree. If the binary search tree is not balanced then the search will take more time as it will not ignore irrelevant values. That means that our search performance will be decreased compared with a balanced tree.

Popular algorithms to balance the tree are

  1. AVL Tree
  2. Red Black Trees

Deletion

  1. Node is a leaf node
    Set the node to null
  2. Node has a single child
    Update the reference of parent of the node to child of the Node
  3. Node has two child
    We look for the largest item in the left subtree or the smallest item in the right subtree and swap with the Node.

Traverse

  1. In order Traverse
    The left subtree is visited first, then the root and later the right sub-tree.
  2. Pre order Traverse
    The root node is visited first, then the left subtree and finally the right subtree.
  3. Post order Traverse
    The root node is visited last, hence the name. First we traverse the left subtree, then the right subtree and finally the root node.

Docker

  • Cheatsheet
  • Architecture
  • Container

Docker

Server ( Docker Daemon)

Docker Server manages creating and maintaining containers using containerd, networking, persistent storage, orchestration and distribution -

REST API

Client (Docker API)

Cheatsheet

# Run a container based on a docker image
docker run -d nginx # -d for running in background
docker run -d --name ashishdotme-nginx -p 9090:80 nginx:latest # expose 80 port from the container and map it to 9090
docker run -d --name ashishdotme-nginx -p 80 nginx:latest # expose 80 port to randomly available port
docker port ashishdotme-nginx 80 # find the port of host machine to binded to 80
docker run -d --name ashishdotme-nginx -p 80 -v c:\nginx:/data nginx:latest # mount host storage for persistent container

# Get details about running container
docker inspect ashishdotme-nginx

# Get logs from a container
docker logs ashishdotme-nginx

# Stop services only
docker-compose stop

# Stop and remove containers, networks..
docker-compose down 

# Down and remove volumes
docker-compose down --volumes 

# Down and remove images
docker-compose down --rmi <all|local> 

# Starts the container and leaves them running
docker-compose up -d

Container

Html

  • Position

Position

CSS treats each HTML element as its own box, which is usually referred to as the CSS Box Model. Block-level items automatically start on a new line like headings while inline items sit within surrounding content. We can use the position property to override the layout of elements.

Position

Relative

When the position of an element is set to relative, it allows you to specify how css should move the element relative to its current position in the normal flow of page using top, bottom, left, right.

Absolute

Absolute removes the element from the normal flow of the document, so surrounding items ignore it. It locks the element in place relative to its parent container.

Fixed

Fixed is type of absolute positioning that locks the element relative to the browser window. The difference between fixed and absolute positions is that an element with a fixed position won't move when the user scrolls.

Float

Float property removes the element from normal flow of a document and push it to either left or right

Javascript

  • Design Patterns
  • Core Concepts

Core Concepts

  • Closure
  • Async Await
  • Callback Hell
  • Generator
  • Babel
  • Prototypes And Classes
  • Hoisting
  • Es6
  • Exports Imports
  • Es5
  • Webpack

Closure

Practical Example (Counter):

function createCounter() {
    let count = 0; // Variable in the outer function
    return function () {
        count++; // Inner function has access to 'count'
        return count;
    };
}

const counter = createCounter(); // Create a closure
console.log(counter()); // Output: 1
console.log(counter()); // Output: 2
console.log(counter()); // Output: 3

Here:

  • createCounter defines a private count variable.
  • The returned function (a closure) remembers count and updates it each time it's called, even though createCounter has finished running.

Async Await

  • Async/await builds on top of promises
  • Enables synchronous style execution of multiple asynchronous methods
  • Functions are prefixed with the "async" keyword
  • Enables the use of the "await" keyword for invoking promisified functions

Babel

Babel is a javascript compiler. It translates the modern javascript features so our code also works on older browser.

Callback Hell

  • The callback pattern is the default pattern for managing the outcome of an asynchronous method
  • Nested callbacks become unmanageable and unreadable
  • Nested callbacks are often termed as "Callback hell"

Example

firstFunction(args, function () {
  secondFunction(args, function () {
    thirdFunction(args, function () {
      // And so on…
    })
  })
})

ES 5

Classes

Classes are passed by value

ES 6

Let vs Const

In javascript, var is used to create variable. With ES6, let and const were introduced. Var still works but its recommended to use let and const. Use const to create constant value whose value never changes and use let to create variables whose value is going to change.

Arrow functions

Arrow function syntax is a bit shorter than the normal syntax since it omits the function keyword. When we use this inside an arrow function it will keep its context and not change it.

const multiply = (number) => number * 2;

Three dots as Rest/Collector

...names in the below example is a collector which collects rest of parameters

var [city, ...names] = ["Pune", "Ashish", "Ansu", "Anju"]
console.log(city); // Pune
console.log(names); // ["Ashish", "Ansu", "Anju]

Three dots as Spread

Spread takes all elements, all properties and distributes them in new array. Spread is use to create new object to prevent reference copying as objects and arrays are reference types. So when we reassign arrays or objects, we are copying the pointer, not the value.

const names = ["Pune", "Ashish", "Ansu", "Anju"]
const updatedNames = [...names, "Patel"]
console.log(updatedNames); // [ 'Pune', 'Ashish', 'Ansu', 'Anju', 'Patel' ]

Destructuring

Destructing allows extracting array elements or object properties and store them in variable.

// Array destructuring
[firstName, lastName] = ["Ashish", "Patel"]
console.log(firstName); // Ashish
console.log(lastName); // Patel

// Object destructuring
const { firstName } = { firstName: "Ashish", lastName: "Patel"}
console.log(firstName) // Ashish

Exports Inports

Default Export

const school = {
    name: "Bhavans"
}

export default school

Export by name


Generators

  • Generators are functions that provide us with an iterator
  • Generator objects can be paused and made to return a value using the yield keyword
  • Calling next() resumes the function and we get successive values
  • Async generators combine the power of async/await and generators

Hoisting

In JavaScript, hoisting refers to the behavior where variable and function declarations (but not initializations) are conceptually moved to the top of their scope (function or global) during the compilation phase.!

var

Has function-level or global scope. This means a variable declared with var is accessible throughout the entire function it's declared in, or even globally if declared outside of any function.

let and const keywords were introduced in ES2015 or ECMAS-6, before this variables were used to be declared only with var.

Prototypes and classes

Generating objects using functions

We can generate objects using function but each time new object is created, there are multiple copies of same functions.

function personCreator(name, age) {
 const newPerson = {};
 newPerson.name = name;
 newPerson.age = age;
 newPerson.increaseAge = function() {
  newPerson.age++;
 };
 return newPerson;
};
const ashish = personCreator("Ashish Patel", 24);
ashish.increaseAge()

Generating objects using prototypes

So we use prototypes to create objects for storing functions with their associated data.

function PersonCreator(name, age){
 this.name = name;
 this.age = age;
}
PersonCreator.prototype.increaseAge = function(){
 this.age++;
};

// Using new creates a new object and returns it
const ashish = new PersonCreator(“Ashish Patel”, 24)
ashish.increaseAge()

Subclassing can be achieved by below code

function PersonCreator(name, age) {
 this.name = name;
 this.age = age;
}

PersonCreator.prototype.increaseAge = function(){
 this.age++;
}

function PersonCreatorWithCaste(name, age, caste){
 PersonCreator.call(this, name, age);
 this.caste = caste;
}

PersonCreatorWithCaste.prototype = Object.create(PersonCreator.prototype);
PersonCreatorWithCaste.prototype.displayCaste = function(){
 console.log(this.caste);
}

const ashish = new PersonCreatorWithCaste("Ashish Patel", 25, "Agharia");
console.log(ashish.age);
ashish.increaseAge();
console.log(ashish.age);
ashish.displayCaste();

Generating objects using classes

Class was introduced with ES2015, it let us write the shared methods in one place instead of writing the constructor and shared methods separately.

class PersonCreator {
 constructor (name, age){
   this.name = name;
   this.age = age;
 }
 increaseAge(){
   this.age++;
 }
}
const ashish = new PersonCreator("Ashish Patel", 24);
ashish.increaseAge();

Subclassing in ES2015 can be achieved by below code

class PersonCreator {
 constructor (name, age){
   this.name = name;
   this.age = age;
 }
 increaseAge(){
   this.age++;
 }
}

class PersonCreatorWithCaste extends PersonCreator {
  constructor(name, age, caste){
    super(name, age);
    this.caste = caste;
  }

  displayCaste(){
    console.log(this.caste);
  }

}

const ashish = new PersonCreatorWithCaste("Ashish Patel", 24, "Agharia");
console.log(ashish.age);
ashish.increaseAge();
console.log(ashish.age);
ashish.displayCaste();

Webpack

We need bundler because we want to write modular code and split our code in multiple files so that each file has clear task, We use webpack to bundle all our files into couple of files in end for deployment. Bundling helps in reducing the number of requests browser has to make to get all the files.

Webpack also allows us to apply a couple of other build steps before it does the bundling.

Design Patterns

  • Adapter Pattern
  • Decorator Pattern
  • Proxy Pattern
  • Builder Pattern
  • Singleton Pattern
  • Factory

Adapter Pattern

Adapter Pattern is an abstraction for nasty or 3rd party code, you need in your main clean codebase.

It is basically a wrapper around a particular class or object, which provides a different API and utilizes the object’s original one in the background.

Use Cases

  • It is used to create a bridge between two different interfaces
  • Removes incompabilities between the interfaces
  • Prevents or minimizes refactoring client application code
  • Lets you build packages with an opinionated API, with custom adapters for maxmium compability
// index.js
import { v4 as uuidv4 } from 'uuuid'

console.log(uuidv4()) // without adapter pattern
// uuid.js
import { v4 as uuidv4 } from 'uuuid'

class uuid {
  generate() {
    return uuidv4()
  }
}

export default new uuid()
// App.js
import uuid from './uuid

console.log(uuid.generate())

Builder Pattern

  • It enables the creation of an easy to use interface to a complex process.
  • By Introducing a step by step workflow, npm packages can be made easy to understand and consume
// Course.js
class Course {
  constructor(name, sales, isFree = false, price, isCampain = false) {
    this.name = name
    this.sales = sales || 0
    this.isFree = isFree
    this.price = price || 0
    this.isCampain = isCampain // Advertising Campaign
  }

  toString() {
    return console.log(JSON.stringify(this))
  }
}
module.exports = Course
// CourseBuilder.js
const Course = require('./course')

class CourseBuilder {
  constructor(name, sales = 0, price = 0) {
    this.name = name
    this.sales = sales
    this.price = price
  }

  makePaid(price) {
    this.isFree = false
    this.price = price
    return this
  }

  makeCampain() {
    this.isCampain = true
    return this
  }

  build() {
    return new Course(this)
  }
}

module.exports = CourseBuilder
//App.js
const CourseBuilder = require('./CourseBuilder')

//const course_1 = new CourseBuilder('Design Patterns 1', 0, true, 149 , true);
//const course_2 = new CourseBuilder('Design Patterns 1', 0,false, 0, false);
const course_1 = new CourseBuilder('Design Patterns 1').makePaid(100).makeCampain().build()
const course_2 = new CourseBuilder('Design Patterns 2').build()

course_1.toString()
course_2.toString()

Decorator Pattern

Decorator Pattern is designed to provide you with a clean way of extending abilities of your original Object or Component, without impacting its initial state or structure.

  • It ingests a function and returns back a function
  • Decorators can be used to add features and function to existing objects dynamically
  • Implemented as high order functions
// User.js

class User {
  constructor(firstName, lastName, title) {
    this.firstName = firstName
    this.lastName = lastName
    this.title = title
  }

  getFullName() {
    return `${this.firstName} ${this.lastName}`
  }
}
// UserDecorator.js

class UserDecorator {
  constructor(user) {
    this.user = user
  }

  getFullName() {
    return this.user.getFullName()
  }
}
// UserFullNameWithTitleDecorator.js

class UserFullNameWithTitleDecorator extends UserDecorator {
  getFullName() {
    return `${this.user.title} ${this.user.getFullName()}`
  }
}
// App.js

const user = new User('Arthur', 'Frank', 'Mr')
user.getFullName()

const decoratedUser = new UserFullNameWithTitleDecorator(user)
decoratedUser.getFullName()

Factory Pattern

In factory pattern, we create objects without exposing the creation logic to the code that requires the object to be created.

  • It provides an interface for constructing pre configured objects
  • Code is cleaner
  • It allows you to offer an easy to understand interface to your packages function
// Factory.js

function deliveryFactory(address, item) {
  if (distance > 10 && distance < 50) {
    return new DeliveryByCar(address, item)
  }

  if (distance > 50) {
    return new DeliveryByTruck(address, item)
  }

  return new DeliveryByBike(address, item)
}

class DeliveryByBike {
  constructor(address, item) {
    this.address = address
    this.item = item
  }
}

class DeliveryByTruck {
  constructor(address, item) {
    this.address = address
    this.item = item
  }
}

class DeliveryByCar {
  constructor(address, item) {
    this.address = address
    this.item = item
  }
}

const newDelivery = deliveryFactory('121 baily ave, Toronto, canada', 'nitendo 360')

Abstract Factory Pattern

In factory pattern, we take care of creating objects of same family whereas in abstract factory pattern we will provide a constructor for creating families of related objects, without specifying concrete classes or constructors.

function abstractFactory(address, item, options) {
  if (options.isSameday) {
    return sameDayDeliveryFactory(address, item)
  }
  if (options.isExpress) {
    return expressDeliveryFactory(address, item)
  }

  return deliveryFactory(address, item)
}

Proxy Pattern

A proxy is an object that has the same interface as another object and is used in place of that other object. It provides a surrogate or placeholder for another object to control access to it. It intends to add a wrapper and delegation to protect the real component from undue complexity.

  • It allows us to create placeholder wrappers for objects
  • A proxy Object allows external access control to the object
  • Implements the same interface as the original object

Use cases

  • Caching remotely accessed data
  • Optimize or pre process data on access
  • Logging
  • Encryption
  • Simulating private and inaccessible properties
  • Data validation
// External API Service
function CryptocurrencyAPI() {
  this.getValue = function (coin) {
    console.log('Calling External API...')
    switch (coin) {
      case 'Bitcoin':
        return '$8,500'
      case 'Litecoin':
        return '$50'
      case 'Ethereum':
        return '$175'
      default:
        return 'NA'
    }
  }
}

function CryptocurrencyProxy() {
  this.api = new CryptocurrencyAPI()
  this.cache = {}

  this.getValue = function (coin) {
    if (this.cache[coin] == null) {
      this.cache[coin] = this.api.getValue(coin)
    }
    return this.cache[coin]
  }
}

const proxy = new CryptocurrencyProxy()
console.log(proxy.getValue('Bitcoin'))
console.log(proxy.getValue('Litecoin'))

Singleton Design Pattern

  • Singletons are objects that can only have a single instance, with a single point of access
  • The module system in nodejs offers a rudimentary implementation of a singleton
  • In modules, single instance of class is created and cached

Example

// CashRegister.js

let cash = 0

const CashRegister = {
  credit(amount) {
    cash = cash + amount
    return cash
  },
  debit(amount) {
    if (amount <= cash) {
      cash = cash - amount
      return true
    } else {
      return false
    }
  },
  total() {
    return cash
  },
}

module.exports = CashRegister
// App.js

const cashRegister = require('./CashRegister')
const cashRegister2 = require('./CashRegister')

cashRegister.credit(10)
cashRegister2.credit(20)
cashRegister.debit(5)
console.log(cashRegister.total()) // answer is 25 as both object are using same instance

Kafka

  • Topic

Topic

  • Topics are like table, provides scalability
  • Each topic has 1 or more than 1 partition
  • Each partition is an ordered, immuatable sequence of records
  • Each record is assigned a sequential number called offset
  • Each partition is independent of each other
  • Ordering is guaranteed only at the partition level
  • Partition continously grows as new records are produced
  • All the records are persisted in a distributed commit log in the file system where Kafka is installed

    Misc

    • Maths
    • Health
    • English
    • Gym

    English

    • Vocabulary
    • Prepositions

    Prepositions

    Vocabulary

    1. menace - be a threat

    2. adamant - refusing to change ones mind

    3. avert - turn away

    4. dwindle - get smaller

    Gym

    • Shoulder
    • Chest
    • Tricep
    • Workout Plan

    Chest

    Bench press

    Incline bench press

    Dumbell fly

    Pushup

    Pec deck

    Shoulder

    Reverse Fly

    Overhead Press

    Lateral Raise

    Tricep

    Tricep Extensions

    Workout Plan

    6 Day PPL Routine

    DayExercise
    Day 1Chest, Shoulder and Triceps
    Day 2Back, Biceps and Abs
    Day 3Legs
    Day 4Chest, Shoulder and Triceps
    Day 5Back, Biceps and Abs
    Day 6Legs
    Day 7OFF

    3-Day PPL Routine

    DayExercise
    Day 1Chest, Shoulder and Triceps
    Day 2Back, Biceps and Abs
    Day 3Legs

    Full Workout Routine

    Exercises

    DayExercise
    Day 1Barbell bench press, Dumbbell overhead press, Dumbbell Fly, Triceps extensions, lateral raises
    Day 2Bent over row, Cable Row, Biceps Curl, Hammer Curl, Reverse Fly
    Day 3Front Squat, Leg Curl, Leg Extension, Calf Raise

    Muscle Group

    DayMuscle
    Day 1Chest, Shoulder, Chest, Triceps, Shoulder
    Day 2Back, Back, Bicep, Bicep, Back
    Day 3Back, Leg, Leg, Leg
    • Are Push/Pull/Legs Routines Good for Building Muscle?

    Health

    • Skincare

    Skincare

    ProductUses
    Salicylic AcidTreats Pimple
    NiacinamideHydration
    Glycolic AcidExfoliation of the top layer of the skin
    RetinolSkin looks firm
    Hyaluronic acidHydration

    Maths

    • Misc
    • Logarithm

    Logarithm

    Logarithm, the exponent or power to which a base must be raised to yield a given number. The default base is 10.

    Examples




    Random Formulas

    Inverse a Number

    Mod(%) operation on a number by 10 returns the rightmost digit Divide(/) operation on a number by 10 removes the rightmost digit

    Num = 126


    Reversing 126 % 10 = 6
    int(126/10) = 12
    12 % 10 = 2
    int(12/10) = 1
    1 % 10 = 1

    Personal

    • Info
    • Shortcuts
    • Cli
    • Subscriptions
    • Setup

    CLI

    List

    CLIDescription
    HoarderBookmarks manager
    OrganizeOrganize files

    Common Info

    Name: Ashish Patel

    Ring size

    Eu size - 62
    Diameter - 19.8 cm
    UK - T 1/2
    US - 10

    Shortcuts

    Custom

    ShortcutDescription
    Ctrl + Shift + pProofread

    Glazewm

    ShortcutDescription
    Alt + EnterWezterm
    Alt + Shift + EnterMicrosoft Terminal
    Alt + Ctrl + GVisual Studio Code
    Alt + Ctrl + EMicrosoft Edge

    Browser

    ShortcutDescription
    Ctrl + LGo to Address Bar
    Ctrl + Shift + CClose all tabs (Install extension)

    TickTick

    ShortcutDescription
    Ctrl + Shift + EShow window
    Alt + Shift + AQuick Add

    Windows

    ShortcutDescription
    Windows + VPaste window
    Windows + EExplorer window
    Windows + .Emoji Window

    Visual Studio

    ShortcutDescription
    F12Go to Definition
    Ctrl + F12Go to Implementation

    Subscriptions

    ServiceCostBilling CycleNotes
    48.ie€12,99Monthly
    Domain renewals€25AnnualExpiry 4 October 2025
    Wifi€35Monthly
    VPS€8.3MonthlyPaid till November 2024
    Google One€3Monthly
    Apple iCloud€3Monthly
    Youtube Premium€3Monthly
    TickTick€4Monthly
    Gym€31Monthly

    Total Monthly -  ≈€103.2

    Setup

    • Macbook

    Macbook Setup

    Show path bar

    defaults write com.apple.finder ShowPathbar -bool true

    XCode

    xcode-select --install

    Homebrew

    /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

    Tools

    brew tap caskroom/cask
    brew install git
    brew cask install google-chrome 
    brew cask install spectacle
    brew cask install iterm2
    

    Git

    git config --global user.name "Ashish Patel"
    git config --global user.email "ashishsushilpatel@gmail.com"
    

    Shell

    zsh

    # Install zsh
    brew install zsh zsh-completions
    
    # oh-my-zsh
    sh -c "$(curl -fsSL https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh)"
    
    # Change shell
    chsh -s /bin/zsh
    
    # Restart iterm
    # Install auto suggestions plugin
    git clone git://github.com/zsh-users/zsh-autosuggestions $ZSH_CUSTOM/plugins/zsh-autosuggestion
    
    # Install poweling font
    # Change font to Meslo LG L for powerline
    https://github.com/powerline/fonts
    
    # Use following config in zshrc 
    plugins=(git colored-man colorize github jira vagrant virtualenv pip python brew osx zsh-syntax-highlighting zsh-autosuggestions)
    ZSH_THEME="agnoster"
    DEFAULT_USER=$(whoami)
    
    

    Nodejs

    # Install nvm
    curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.0/install.sh | bash
    # Install latest lts nodeks
    nvm install -lts
    brew install yarn --without-node
    

    Docker

    brew cask install docker
    

    VS Code

    # Press command + shift + p and click on Shell Command : Install code in PATH
    ```<style>
      .scroll-to-top {
        font-size: 2.5rem;
        width: 3.2rem;
        height: 3.2rem;
        display: none;
        align-items: center;
        justify-content: center;
        position: fixed;
        padding: 0.75rem;
        bottom: 4rem;
        right: calc(1.25rem + 90px + var(--page-padding));
        z-index: 999;
        cursor: pointer;
        border: none;
        color: var(--bg);
        background: var(--fg);
        border-radius: 50%;
      }
      .scroll-to-top.hidden {
        display: none;
      }
      .scroll-to-top i {
        transform: translateY(-2px);
      }
      @media (min-width: 1080px) {
        .scroll-to-top {
          display: flex;
        }
      }
    </style>
    <button type="button" aria-label="scroll-to-top" class="scroll-to-top hidden" onclick="scrollToTop()">
      <i class="fa fa-angle-up"></i>
    </button>
    <script>
      const scrollToTop = () => window.scroll({ top: 0, behavior: "smooth" });
      window.addEventListener("scroll", () => {
        const button = document.querySelector(".scroll-to-top");
        button.classList.toggle("hidden", window.scrollY < 200);
      });
    </script>
    <style>
      footer {
        text-align: center;
        text-wrap: balance;
        margin-top: 5rem;
        display: flex;
        flex-direction: column;
        justify-content: center;
        align-items: center;
      }
      footer p {
        margin: 0;
      }
    </style>
    <footer><p>Copyright © 2024 • Created with ❤️ by <a href="https://ashish.me">Ashish Patel</a></p>
    </footer>
    

    React

    • Lifecycle
    • Redux Sideeffects
    • Testing
    • Binding
    • Redux

    Binding

    Default binding

    function display(){
     console.log(this); // 'this' will point to the global object
    }
    display();
    

    Implicit binding

    var obj = {
     name: 'Saurabh',
     display: function(){
       console.log(this.name); // 'this' points to obj
      }
    };
    obj.display(); // Saurabh
    
    var name = "uh oh! global";
    var outerDisplay = obj.display;
    outerDisplay(); // uh oh! global
    
    function setTimeout(callback, delay){
       callback(); // callback = obj.display;
    }
    setTimeout( obj.display, 1000 );
    
    var name = "uh oh! global";
    setTimeout( obj.display, 1000 );
    // uh oh! global
    

    Explicit hard binding

    var name = "uh oh! global";
    obj.display = obj.display.bind(obj); 
    var outerDisplay = obj.display;
    outerDisplay();
    // Saurabh
    

    Lifecycle

    Create

    Contructor(props)

    • It's a default es6 class feature
    • Used to call super(props)
    • You can set up state
    • You should not cause side effects

    ComponentWillMount()

    • You can update state
    • You can do last minute optimization
    • You should not cause side effects

    Render()

    • You can structure your code here

    ComponentDidMount()

    • You can cause side effects
    • You can call APIS and do data modification
    • You should not update state though as it triggers re render

    Update

    ComponentWillReceiveProps()

    • You can sync state to props
    • You should not cause side effects

    ShouldComponentUpdate()

    • You can decide wether to continue or not
    • You should not cause side effects

    ComponentWillUpdate()

    • You can sync state to props
    • You should not cause side effects

    ComponentDidUpdate()

    • You can cause side effects
    • You should not update state as it triggers re render

    ComponentWillUnmount()

    Redux Sideeffects

    Redux is a predictable state container for JavaScript apps which makes state management easier but the actions dispatched via Redux are synchronous. For network calls, we need the ability to dispatch actions asynchronously. Dispatching actions asynchronously can be done by popular middlewares like i.e Redux Thunk and Redux Saga. Redux middleware is code that intercepts actions coming into the store via the dispatch() method.

    Sideeffects

    When doing the fetch request in redux, we can't be sure what the call will return or that it will even succeed. This is known as a side effect.

    Redux Thunk

    Redux Thunk uses promises for async actions.

    const getPosts = ({dispatch}) => {
      dispatch({type: 'POSTS_LOADING'})  
      fetch('api/posts')
        .then(res => dispatch({type: 'GET_POSTS', payload: res.data}))
        .catch(err => dispatch({type: 'GET_ERRORS', payload: {}))
    }
    
    store.dispatch(getPosts)
    

    Redux Saga

    Redux Saga uses generators for async actions.

    export default function* onGetPosts() {
      yield takeLatest('RECORDS/FETCH', function getPosts() {
    
        try {
            const response = yield call(fetch, 'api/posts');
            const responseBody = response.json();
        } catch (e) {
            yield put(fetchFailed(e));
            return;
        }
    
        yield put(setRecords(responseBody.records));
      });
    }
    

    Redux

    Redux is used to change the state of application.

    Three principles of Redux

    1. Single source of truth - State of whole application is stored in a single tree
    2. State is read only - Only by emitting action, we can change the state of applcation
    3. Changes are made with pure functions - Reducers which are pure functions can only change the state

    Connect

    mapDispatchToProps

    • It's a method you provide to connect
    • Recieves dispatch as an argument
    • Allows you to create functions which dispatch actions
    • It can return an object which is passed to component as props.

    Testing

    Testing with Mocha, Chai, Enjyme and Sinon

    Mocha

    Mocha is a javascript test framework which supports asynchronous testing, test coverage reports and use of any assertion library.

    Chai

    Chai is a BDD/TDD assertion library

    Enzyme

    Enjyme is a javascript testing utility for React that makes it easier to traverse and manipulate react component's output

    Sinon

    Sinon is used for Spies/Stubs/Mocks. It can also fake ajax calls and timers. So basically it allows you solve problems which occur due to external dependencies.

    1. Spies - offers information about function cals
    2. Stubs - Which are like spies but completely replace the functions
    3. Mocks - It replaces the whole object by combining spies and stubs

    Adding Mocha, Chai, Enjyme and Sinon to the project

    yarn add --dev chai
    yarn add --dev enzyme
    yarn add --dev enzyme-adapter-react-16
    yarn add --dev mocha
    yarn add --dev @types/chai
    yarn add --dev @types/enzyme
    yarn add --dev @types/enzyme-adapter-react-16
    yarn add --dev @types/mocha
    yarn add --dev chai enzyme enzyme-adapter-react-16 mocha
    yarn add --dev @types/chai @types/enzyme @types/enzyme-adapter-react-16 @types/mocha
    ```<style>
      .scroll-to-top {
        font-size: 2.5rem;
        width: 3.2rem;
        height: 3.2rem;
        display: none;
        align-items: center;
        justify-content: center;
        position: fixed;
        padding: 0.75rem;
        bottom: 4rem;
        right: calc(1.25rem + 90px + var(--page-padding));
        z-index: 999;
        cursor: pointer;
        border: none;
        color: var(--bg);
        background: var(--fg);
        border-radius: 50%;
      }
      .scroll-to-top.hidden {
        display: none;
      }
      .scroll-to-top i {
        transform: translateY(-2px);
      }
      @media (min-width: 1080px) {
        .scroll-to-top {
          display: flex;
        }
      }
    </style>
    <button type="button" aria-label="scroll-to-top" class="scroll-to-top hidden" onclick="scrollToTop()">
      <i class="fa fa-angle-up"></i>
    </button>
    <script>
      const scrollToTop = () => window.scroll({ top: 0, behavior: "smooth" });
      window.addEventListener("scroll", () => {
        const button = document.querySelector(".scroll-to-top");
        button.classList.toggle("hidden", window.scrollY < 200);
      });
    </script>
    <style>
      footer {
        text-align: center;
        text-wrap: balance;
        margin-top: 5rem;
        display: flex;
        flex-direction: column;
        justify-content: center;
        align-items: center;
      }
      footer p {
        margin: 0;
      }
    </style>
    <footer><p>Copyright © 2024 • Created with ❤️ by <a href="https://ashish.me">Ashish Patel</a></p>
    </footer>
    

    Software Development

    • Uml
    • Computer Architecture
    • Oops

    Computer Architecture

    • Memory

    Memory

    Heap and stack are generic terms for ways in which memory can be allocated. Stack is more faster than heap because

    Stack

    In Stack, the items sit on top of the other in they order they are are placed and you can only remove the top one. No table is needed to maintain stack, we just need pointer to the top of stack. Programs have call stack which stores information about which functions call other functions and return. It also stores local variables.

    Heap

    In Heap, there is no particular order to the way items are placed. You can remove item in any order. Heap allocation requires maintaining what memory is allocated and what isn't. Memory is allocated dynamically and randomly.

    Oops

    • Abstraction
    • Shopping Cart Uml
    • Bank Uml

    Abstraction

    Abstraction is basically capturing the core data of an object and ignoring the details.

    In the above example, When student class need to work with Course class, it can do so with directly referencing course class, without worrying about how the course details are internall managed, stored, etc.

    Advantages

    • Reduces complexity
    • Increases Efficiency

    Banl UML

    UML Diagram

    
    classDiagram
    
    class Bank {
    -balance: int
    +getBalance: int
    }
    
    class Atm {
    -amount: int
    +withdraw(amount): int
    }
    
    class Customer {
    -name: string
    -accountNumber: int
    +getCustomer(accountNumber): string
    }
    
    class Account {
    -accountNumber: int
    }
    
    class Transaction {
    -accountNumber: int
    -transactionId: int
    }
    
    class CheckingAccount {
    -accountNumber: int
    }
    
    class SavingsAccount {
    -accountNumber: int
    }
    
    CheckingAccount <|-- Account
    SavingsAccount <|-- Account
    Bank *-- Account
    Customer *-- Account
    Bank --> Atm
    Transaction ..> Atm
    ```<style>
      .scroll-to-top {
        font-size: 2.5rem;
        width: 3.2rem;
        height: 3.2rem;
        display: none;
        align-items: center;
        justify-content: center;
        position: fixed;
        padding: 0.75rem;
        bottom: 4rem;
        right: calc(1.25rem + 90px + var(--page-padding));
        z-index: 999;
        cursor: pointer;
        border: none;
        color: var(--bg);
        background: var(--fg);
        border-radius: 50%;
      }
      .scroll-to-top.hidden {
        display: none;
      }
      .scroll-to-top i {
        transform: translateY(-2px);
      }
      @media (min-width: 1080px) {
        .scroll-to-top {
          display: flex;
        }
      }
    </style>
    <button type="button" aria-label="scroll-to-top" class="scroll-to-top hidden" onclick="scrollToTop()">
      <i class="fa fa-angle-up"></i>
    </button>
    <script>
      const scrollToTop = () => window.scroll({ top: 0, behavior: "smooth" });
      window.addEventListener("scroll", () => {
        const button = document.querySelector(".scroll-to-top");
        button.classList.toggle("hidden", window.scrollY < 200);
      });
    </script>
    <style>
      footer {
        text-align: center;
        text-wrap: balance;
        margin-top: 5rem;
        display: flex;
        flex-direction: column;
        justify-content: center;
        align-items: center;
      }
      footer p {
        margin: 0;
      }
    </style>
    <footer><p>Copyright © 2024 • Created with ❤️ by <a href="https://ashish.me">Ashish Patel</a></p>
    </footer>
    

    Shopping cart UML

    Shopping cart

    classDiagram
    class Item {
    -itemId
    +isRestricted()
    }
    
    class Customer {
    -name
    +getAddress()
    }
    class Address {
    -country
    -state
    }
    Customer "1" *-- "1" Address : composition 
    

    Uml

    • Uml Class Restaurant
    • Uml Class
    • Assets
    • Uml Class Library

    Library UML

    Library Management System

    Restaurant UML

    Resturant Management System

    UML Concepts

    Types of Relationships

    1. Association

    Association is: Class A uses Class B.

    Example:

    • Employee uses Bus/train Services for transportation.
    • Computer uses keyboard as input device

    And in In UML diagram Association is denoted by a normal arrow head.

    2. Aggregation

    Class A contains Class B, or Class A has an instance of Class B.

    An aggregation is used when life of object is independent of container object. But still container object owns the aggregated object.

    So if we delete class A that doesn't mean that class B will also be deleted. E.g. none, or many, teachers can belong to one or many departments.

    The relationship between Teachers and Departments is aggregation.

    3. Composition

    Class A owns Class B.

    E.g. Body consists of Arm, Head, Legs. BankAccount consists of Balance and TransactionHistory.

    So if class A gets deleted then also class B will get deleted.

    Relationships Cheatsheet

    UML Class

    Mermaid class UML diagram syntax

    TypeDescription
    <|--Inheritance
    *--Composition
    o--Aggregation
    -->Association
    --Link (Solid)
    ..>Dependency
    ..|>Realization
    ..Link (Dashed)

    Assets

    • Uml Class Restaurant
    • Uml Class Library

    Sql

    • Joins
    • Basics

    Basics

    Create table

    --Create the main employee table
    CREATE TABLE employee (
      id int PRIMARY KEY,
      name varchar(255),
      age int
    );
    
    --Create the employee age table
    CREATE TABLE employee_age (
      id int PRIMARY KEY,
      age int
    );
    

    Insert

    INSERT INTO employee
      VALUES (1, 'Ashish', 'Pune');
    INSERT INTO employee
      VALUES (2, 'patel', 'Nagpur');
    INSERT INTO employee
      VALUES (4, 'Ansu', 'Bikaner');
    
    INSERT INTO employee_age
      VALUES (1, 23);
    INSERT INTO employee_age
      VALUES (2, 27);
    INSERT INTO employee_age
      VALUES (3, 21);
    

    Select

    SELECT * FROM employee;
    
    idnamecity
    1AshishPune
    2patelNagpur
    4AnsuBikaner
    SELECT * FROM employee_age;
    
    idage
    123
    227
    321
    .scroll-to-top {
    font-size: 2.5rem;
    width: 3.2rem;
    height: 3.2rem;
    display: none;
    align-items: center;
    justify-content: center;
    position: fixed;
    padding: 0.75rem;
    bottom: 4rem;
    right: calc(1.25rem + 90px + var(--page-padding));
    z-index: 999;
    cursor: pointer;
    border: none;
    color: var(--bg);
    background: var(--fg);
    border-radius: 50%;
    }
    .scroll-to-top.hidden {
    display: none;
    }
    .scroll-to-top i {
    transform: translateY(-2px);
    }
    @media (min-width: 1080px) {
    .scroll-to-top {
    display: flex;
    }
    }

    Joins

    SQL Joins is used to combine data or rows from two or more tables based on a common field between them. Different types of Joins are:

    Employee Table

    idnamecity
    1AshishPune
    2patelNagpur
    4AnsuBikaner

    Age Table

    idage
    123
    227
    321

    Inner Join

    It selects all rows from both the tables as long as the condition satisfies. Only using JOIN is same as INNER JOIN

    SELECT
      employee_age.id,
      employee.name,
      employee.city
    FROM employee
    INNER JOIN employee_age
      ON employee.id = employee_age.id;
    
    idnamecityage
    1AshishPune23
    2patelNagpur27

    Left Join

    This join returns all the rows of the table on the left side of the join and matching rows for the table on the right side of join.

    SELECT
      employee_age.id,
      employee.name,
      employee.city
    FROM employee
    LEFT JOIN employee_age
      ON employee.id = employee_age.id;
    
    idnamecityage
    1AshishPune23
    2patelNagpur27
    NULLAnsuBikanerNULL

    Right Join

    RIGHT JOIN is similar to LEFT JOIN. This join returns all the rows of the table on the right side of the join and matching rows for the table on the left side of join.

    SELECT
      employee_age.id,
      employee.name,
      employee.city
    FROM employee
    RIGHT JOIN employee_age
      ON employee.id = employee_age.id;
    
    idnamecityage
    1AshishPune23
    2patelNagpur27
    3NULLNULL21

    Full Join

    FULL JOIN creates the result-set by combining result of both LEFT JOIN and RIGHT JOIN.

    SELECT
      employee_age.id,
      employee.name,
      employee.city,
      employee_age.age
    FROM employee
    FULL JOIN employee_age
      ON employee.id = employee_age.id;
    
    idnamecityage
    1AshishPune23
    2patelNagpur27
    3NULLNULL21
    NULLAnsuBikanerNULL
    .scroll-to-top {
    font-size: 2.5rem;
    width: 3.2rem;
    height: 3.2rem;
    display: none;
    align-items: center;
    justify-content: center;
    position: fixed;
    padding: 0.75rem;
    bottom: 4rem;
    right: calc(1.25rem + 90px + var(--page-padding));
    z-index: 999;
    cursor: pointer;
    border: none;
    color: var(--bg);
    background: var(--fg);
    border-radius: 50%;
    }
    .scroll-to-top.hidden {
    display: none;
    }
    .scroll-to-top i {
    transform: translateY(-2px);
    }
    @media (min-width: 1080px) {
    .scroll-to-top {
    display: flex;
    }
    }

    System Design

    • Database
    • Basics
    • Availability Vs Consistency Pattern

    Availability vs Consistency

    Consistency Pattern

    Methods to ensure data consistency across distributed systems. Three main types:

    Strong Consistency

    Immediate data updates, high integrity, but low availability and high latency2. Example - when a user initiates a transfer of funds from one account to another, the system immediately updates the balance of both accounts and all other system components are immediately aware of the change. This ensures that all users can see the updated balance of both accounts and prevents any discrepancies.

    Weak Consistency:

    Delayed data updates, high availability, low latency, but potential inconsistencies. Example - When a user plays a game, their actions are immediately visible to other players in the same data center, but if there was a lag or temporary connection loss, the actions may not be seen by some of the users and the game will continue. This can lead to inconsistencies between different versions of the game state, but it also allows for a high level of availability and low latency.

    Eventual Consistency:

    Data updates eventually propagate, balancing availability and integrity, but with possible temporary inconsistencies. Example - An example of eventual consistency is a social media platform where users can post updates, comments, and messages. The platform is designed for high availability and low latency, so the data is stored in multiple data centers around the world. When a user posts an update, the update is immediately visible to other users in the same data center, but it may take some time for the update to propagate to other data centers. This means that some users may see the update while others may not, depending on which data center they are connected to. This can lead to inconsistencies between different versions of the data, but it also allows for a high level of availability and low latency.

    Availability Pattern

    • Failover
      • Description: Automatically switches to a backup system if the primary system fails.
      • Example: A database cluster where, if the primary database fails, a standby replica takes over immediately.
    • Load Balancing
      • Description: Distributes incoming requests across multiple servers to ensure no single server is overwhelmed.
      • Example: A web application uses a load balancer to route user requests to multiple web servers, ensuring the app remains responsive.
    • Caching
      • Description: Stores copies of frequently accessed data to reduce load and latency.
      • Example: A content delivery network (CDN) caches website content closer to users to provide faster access and reduce server load.
    • Replication
      • Description: Duplicates data across multiple systems to ensure availability even if one system goes down.
      • Example: A NoSQL database like Cassandra replicates data across several nodes, so it's still accessible if some nodes fail.

    Basics

    System Design

    System design is the process of defining the elements of a distributed system, as well as their interactions and relationships, in order to satisfy a set of specified requirements. Example of distributed systems - different microservices (accounts, payments, orders, inventory) work together to handle transactions.

    Performance vs Scalability

    • If you have a performance problem, your system is slow for a single user.
    • If you have a scalability problem, your system is fast for a single user but slow under heavy load.

    Latency vs Throughput

    Latency and throughput are two important measures of a system’s performance. Latency refers to the amount of time it takes for a system to respond to a request. Throughput refers to the number of requests that a system can handle at the same time.

    Generally, you should aim for maximal throughput with acceptable latency.

    Availability vs Consistency

    Availability refers to the ability of a system to provide its services to clients even in the presence of failures. This is often measured in terms of the percentage of time that the system is up and running, also known as its uptime.

    Consistency, on the other hand, refers to the property that all clients see the same data at the same time. This is important for maintaining the integrity of the data stored in the system.

    In distributed systems, it is often a trade-off between availability and consistency. Systems that prioritize high availability may sacrifice consistency, while systems that prioritize consistency may sacrifice availability. Different distributed systems use different approaches to balance the trade-off between availability and consistency, such as using replication or consensus algorithms.

    Cap Theorem

    The CAP Theorem states that a distributed system can guarantee only two of the following three properties simultaneously:

    1. Consistency: Every read receives the most recent write or an error.
    2. Availability: Every request receives a response, without guarantee that it contains the most recent write.
    3. Partition Tolerance: The system continues to operate despite network partitions.

    Example

    Imagine a distributed database system spread across multiple servers.

    Scenario: Network Partition

    • Consistency and Partition Tolerance (CP):
      • The system ensures all nodes agree on the latest data, but some requests may fail if parts of the network are inaccessible.
      • Example: A banking system prioritizes consistent account balances over availability.
    • Availability and Partition Tolerance (AP):
      • The system remains operational and responsive even if data isn't consistent across all nodes.
      • Example: A social media platform where users can still post messages, but might see outdated comments during a partition.
    • Consistency and Availability (CA):
      • This combination can only work if there are no network partitions, which is unrealistic for distributed systems.

    In practice, systems choose trade-offs based on specific needs, like prioritizing availability in user-facing applications or consistency in financial transactions.

    Database

    SQL

    ACID Properties

    1. Atomicity

      • Definition: Each transaction is all-or-nothing. If any part of the transaction fails, the entire transaction fails.
      • Example: If you're transferring money between bank accounts, either both the debit and credit occur, or neither does.
    2. Consistency

      • Definition: Transactions must move the database from one valid state to another, maintaining all predefined rules.
      • Example: If a transaction updates an account balance, the total amount of money should remain the same before and after the transaction.
    3. Isolation

      • Definition: Transactions should not affect each other. They should behave as if they're executed sequentially, even if they're run in parallel.
      • Example: If two transactions are occurring simultaneously, one transferring funds and another checking balance, they should not interfere with each other.
    4. Durability

      • Definition: Once a transaction is committed, it remains so, even in the event of a system failure.
      • Example: After a successful transfer, the system crashes. Once back up, the transaction should still be reflected in the database.

    Example

    Imagine a bank transaction where Alice transfers $100 to Bob:

    • Atomicity: If the debit from Alice's account fails, the credit to Bob's account won't happen.
    • Consistency: The total money in Alice's and Bob's accounts remains unchanged.
    • Isolation: Another transaction checking Bob's balance won’t see the transfer until it’s complete.
    • Durability: Once the transfer is confirmed, it remains so despite any crashes.

    These properties ensure that databases operate correctly and predictably, even in complex scenarios.

    Sharding is a database architecture pattern used to horizontally partition data across multiple servers, enabling systems to handle more data and transactions. It improves scalability and performance.

    Sharding

    • Definition: Divides a database into smaller, manageable pieces called "shards."
    • Purpose: Allows distribution of data across multiple machines, balancing the load and reducing bottlenecks.

    Sharding Key

    • Definition: A specific key or column used to determine how data is distributed across shards.
    • Purpose: Ensures that related data is stored together and efficiently queried.

    Sharding Example

    Imagine an e-commerce application with a database table Orders:

    OrderIDUserIDProductAmount
    1101Laptop1200
    2102Phone800
    3103Headphones150
    4101Monitor300
    5104Keyboard100
    1. Choose a Sharding Key: Let's use UserID as the sharding key.

    2. Shard Distribution:

      • Shard 1: Users with IDs from 100 to 199
        • Orders for UserIDs 101 and 102
      • Shard 2: Users with IDs from 200 to 299
        • Orders for UserIDs 201 and 202

    Benefits

    • Scalability: As data grows, add more shards.
    • Performance: Queries are distributed, reducing load on a single server.
    • Fault Tolerance: Failure of one shard doesn't affect others.

    By choosing a proper sharding key, data is balanced across shards, minimizing data transfer and optimizing query performance.

    NOSQL

    Introduction

    1. Scalability
      • Horizontal Scaling: Easily distribute data across multiple servers.
    2. Data Model Flexibility
      • Dynamic Schemas: When your data structure is evolving or unstructured.
    3. Performance
      • High Volume of Reads/Writes: Low-latency requirements for large datasets.
    4. Specific Use Cases
      • Big Data Applications: Handling massive data and analytics.
      • Real-Time Applications: Such as chat apps or IoT platforms.
      • Content Management Systems: With varied and flexible content types.

    Key Considerations

    • Consistency vs. Availability: Decide based on your application needs (CAP theorem).
    • Data Relationships: NoSQL is better for denormalized data but less ideal for complex joins.