1560488127

In this post, we are going to explore non-linear data structures like graphs. Also, we’ll cover the central concepts and typical applications.

You are probably using programs with graphs and trees. Let’s say for instance that you want to know the shortest path between your workplace and home, you can use graph algorithms to get the answer! We are going to look into this and other fun challenges.

In the previous post, we explore linear data structures like arrays, linked lists, sets, stacks and so on. This one builds on top of what we learned.

You can find all these implementations and more in the Github repo:https://github.com/amejiarosario/dsa.js

Here is the summary of the operations that we are going to cover on this post:

Before we dive into interesting graph algorithms, let’s first clarify the naming conventions and graph properties.

A graph is a data structure where a **node** can have zero or more adjacent elements.

The connection between two nodes is called **edge**. Nodes can also be called **vertices**.

The **degree** is the number of edges connected to a vertex. E.g., the `purple`

vertex has a degree of 3 while the `blue`

one has a degree of 1.

If the edges are bi-directional, then we have an **undirected graph**. But, if the edges have a direction, then we have a **directed graph** or **di-graph** for short. You can think of it as a one-way street (directed) or two-way street (undirected).

Vertex can have edges that go to itself (e.g., `blue`

node), this is called **self-loop**.

A graph can have **cycles** which means that if you traverse through the node, you could get the same node more than once. The graph without cycles is called **acyclic graph**.

Also, acyclic undirected graphs are called **tree**. We are going to cover trees in depth in the next post.

Not all vertices have to be connected in the graph. You might have isolated nodes or even separated subgraphs. If all nodes have at least one edge, then we have a **connected graph**. When all nodes are connected to all other nodes, then we have a **complete graph**.

For a complete graph, each node should have `#nodes - 1`

edges. In the previous example, we have seven vertices, so each node has six edges.

When edges have values/cost assigned to them, we say we have a **weighted graph**. If the weight is absent, we can assume it’s 1.

Weighted graphs have many applications depending on the domain where you need to solve a problem. To name a few:

- Airline Traffic (image above)

Node/vertex = AirportEdges = direct flights between two airportsWeight = miles between two airports* GPS Navigation

Node = road intersectionEdge = roadWeight = time required to go from one intersection to another* Networks routing

Node = serverEdge = data linkWeight = connection speed

In general, graphs have many real-world applications like: - Electronic circuits
- Flight reservations
- Driving directions
- Telcom: Cell tower frequency planning
- Social networks. E.g., Facebook uses a graph for suggesting friends
- Recommendations: Amazon/Netflix uses graphs to make suggestions for products/movies
- Graphs help to plan the logistics of delivering goods

We just learned the basics of graphs and some applications. Let’s cover how to represent graphs in JavaScript.

There are two primary ways of representing graph:

- Adjacency list
- Adjacency Matrix

Let’s explain it with the following directed graph (digraph) as an example:

We digraph with 4 nodes. When a vertex has a link to itself (e.g. `a`

) is called **self-loop**.

The adjacency matrix is one way of representing a graph using a two-dimensional array (NxN matrix). In the intersection of nodes, we add 1 (or other weight) if they are connected and `0`

or `-`

if they are not connected.

Using the same example as before, we can build the following adjacency matrix:

```
a b c d e
a 1 1 - - -
b - - 1 - -
c - - - 1 -
d - 1 1 - -
```

As you can see, the matrix list all nodes horizontally and vertically. If there a few connections we called **sparse graph** if there are many connections (close to the max number of links) we called it **dense graph**. If all possible connections are reached, then we have a **complete graph**.

It’s important to notice that for undirected graphs the adjacency matrix will **always** be symmetrical by the diagonal. However, that’s not always the case on a digraph (like our example).

What is the time complexity of finding connections of two vertices?

Querying if two nodes are connected in an adjacency matrix takes a constant time or

O(1).

What is the space complexity?

Storing a graph as an adjacency matrix has a space complexity of

O(n2), where`n`

is the number of vertices. Also, represented asO(|V|2)

What is the runtime to add a vertex?

The vertices are stored as a `<em>V</em>`

x`<em>V</em>`

matrix. So, every time a vertex is added, the matrix needs to be reconstructed to a `<em>V+1</em>`

x`<em>V+1</em>`

.

Adding a vertex on an adjacency matrix is

O(|V|2)

What about getting the adjacent nodes?

Since the matrix has a VxV matrix, to get all the adjacent nodes to a given vertex, we would have to go to the node row and get all its edges with the other nodes.

In our previous example, let’s say we want all the adjacent nodes to `b`

. We have to get the full row where b is with all the other nodes.

```
a b c d e
b - - 1 - -
```

We have to visit all nodes so,

Getting adjacent nodes on an adjacency matrix is

O(|V|)

Imagine that you need to represent Facebook network as a graph. You would have to create a matrix of 2 billion x 2 billion, where most of it would be empty! Nobody would know everybody else just a few thousands at most.

In general, we deal with sparse graphs so the matrix will waste a lot of space. That’s why in most implementation we would use an adjacency list rather than the matrix.

Adjacency List is one of the most common ways to represent graphs. Each node has a list of all the nodes connected to it.

Graphs can be represented as an adjacency list using an Array (or HashMap) containing the nodes. Each of these node entries includes a list (array, linked list, set, etc.) that list its adjacent nodes.

For instance, in the graph above we have that `a`

has a connection to `b`

and also a self-loop to itself. In turn, `b`

has a connection to `c`

and so on:

```
a -> { a b }
b -> { c }
c -> { d }
d -> { b c }
```

As you can imagine if you want to know if a node is connected to another node, you would have to go through the list.

Querying if two nodes are connected in an adjacency list is

O(n), where`n`

is the number of vertices. Also represented asO(|V|)

What about the space complexity?

Storing a graph as an adjacency list has a space complexity of

O(n), where`n`

is the sum of vertices and edges. Also, represented asO(|V| + |E|)## 4. Adjacency List Graph HashMap Implementation

The adjacency list is the most common way of representing graphs. There are several ways to implement the adjacency list:

One of them is using a HashMap. The `key`

is the value of the node, and the `value`

is an array of adjacency.

```
const graph = {
a: ['a', 'b'],
b: ['c'],
c: ['d'],
d: ['b', 'c']
}
```

Graph usually needs the following operations:

- Add and remove vertices
- Add and remove edges

Adding and removing vertices involves updating the adjacency list.

Let’s say that we want to remove the vertex `b`

. We could do `delete graph['b'];`

, however, we still have to remove the references on the adjacency list on `d`

and `a`

.

Every time we remove a node, we would have to iterate through all the nodes’ list *O(|V| + |E|)*. Can we do better? We will answer that soon, but first, let’s *implement our list in a more object-oriented way so we can swap implementations easily.

Let’s start with the `Node`

class that holds the vertex’s value and its adjacent vertices. We can also have helper functions for adding and removing adjacent nodes from the list.

```
class Node {
constructor(value) {
this.value = value;
this.adjacents = []; // adjacency list
}
addAdjacent(node) {
this.adjacents.push(node);
}
removeAdjacent(node) {
const index = this.adjacents.indexOf(node);
if(index > -1) {
this.adjacents.splice(index, 1);
return node;
}
}
getAdjacents() {
return this.adjacents;
}
isAdjacent(node) {
return this.adjacents.indexOf(node) > -1;
}
}
```

Notice that `adjacent`

runtime is *O(1)*, while `remove adjacent`

is *O(|E|)*. What if instead of an array use a HashSet 🧐? It could be *O(1)*. But, let first get it working and later we can make it faster.

Make it work. Make it right. Make it faster.

Ok, now that we have the`Node`

class, let’s build the Graph class that can perform operations such as adding/removing vertices and edges.

**Graph.constructor**

```
class Graph {
constructor(edgeDirection = Graph.DIRECTED) {
this.nodes = new Map();
this.edgeDirection = edgeDirection;
}
// ...
}
Graph.UNDIRECTED = Symbol('directed graph'); // one-way edges
Graph.DIRECTED = Symbol('undirected graph'); // two-ways edges
```

The first thing that we need to know is if the graph is directed or undirected. That makes a difference when we are adding edges.

**Graph.addEdge**

To add an edge we need two nodes. One is the source, and the other is the destination.

```
addEdge(source, destination) {
const sourceNode = this.addVertex(source);
const destinationNode = this.addVertex(destination);
sourceNode.addAdjacent(destinationNode);
if(this.edgeDirection === Graph.UNDIRECTED) {
destinationNode.addAdjacent(sourceNode);
}
return [sourceNode, destinationNode];
}
```

We add an edge from the source vertex to the destination. If we have an undirected graph, then we also add from target node to source since it’s bidirectional.

The runtime of adding an edge from a graph adjacency list is:

O(1)

If we try to add an edge and the nodes don’t exist, we need to create them first. Let’s do that next!

**Graph.addVertex**

The way we create a node is that we add it to the `this.nodes`

Map. The map store a key/value pair, where the `key`

is the vertex’s value while the map `value`

is the instance of the node class. Take a look at line 5-6:

```
addVertex(value) {
if(this.nodes.has(value)) {
return this.nodes.get(value);
} else {
const vertex = new Node(value);
this.nodes.set(value, vertex);
return vertex;
}
}
```

If the node already exists we don’t want to overwrite it. So, we first check if it already exists and if it doesn’t, then we create it.

The runtime of adding a vertex from a graph adjacency list is:

O(1)

**Graph.removeVertex**

Removing a node from the graph, it’s a little bit more involved. We have to check if the node to be deleted it’s in use as an adjacent node.

```
removeVertex(value) {
const current = this.nodes.get(value);
if(current) {
for (const node of this.nodes.values()) {
node.removeAdjacent(current);
}
}
return this.nodes.delete(value);
}
```

We have to go through each vertex and then each adjacent node (edges).

The runtime of removing a vertex from a graph adjacency list is

O(|V| + |E|)

Finally, let’s remove implement removing an edge!

**Graph.removeEdge**

Removing an edge is pretty straightforward and similar to `addEdge`

.

```
removeEdge(source, destination) {
const sourceNode = this.nodes.get(source);
const destinationNode = this.nodes.get(destination);
if(sourceNode && destinationNode) {
sourceNode.removeAdjacent(destinationNode);
if(this.edgeDirection === Graph.UNDIRECTED) {
destinationNode.removeAdjacent(sourceNode);
}
}
return [sourceNode, destinationNode];
}
```

The main difference between `addEdge`

and `removeEdge`

is that:

- If the vertices don’t exist, we won’t create them.
- We use
`Node.removeAdjacent`

instead of`Node.addAdjacent`

.

Since `removeAdjacent`

has to go through all the adjacent vertices we have the following runtime:

The runtime of removing an edge from a graph adjacency list is

O(|E|)

We are going to explore how to search for values from a node.

Breadth-first search is a way to navigate a graph from an initial vertex by visiting all the adjacent nodes first.

Let’s see how we can accomplish this in code:

```
*bfs(first) {
const visited = new Map();
const visitList = new Queue();
visitList.add(first);
while(!visitList.isEmpty()) {
const node = visitList.remove();
if(node && !visited.has(node)) {
yield node;
visited.set(node);
node.getAdjacents().forEach(adj => visitList.add(adj));
}
}
}
```

As you can see, we are using a `Queue`

where the first node in is also the first node to be visited (FIFO).

We are as well using JavaScript generators, notice the `*`

in front of the function. This generator iterates one value at a time. That’s useful for large graphs (millions of nodes) because in most cases you don’t need to visit every single node.

This an example of how to use the BFS that we just created:

```
const graph = new Graph(Graph.UNDIRECTED);
const [first] = graph.addEdge(1, 2);
graph.addEdge(1, 3);
graph.addEdge(1, 4);
graph.addEdge(5, 2);
graph.addEdge(6, 3);
graph.addEdge(7, 3);
graph.addEdge(8, 4);
graph.addEdge(9, 5);
graph.addEdge(10, 6);
bfsFromFirst = graph.bfs(first);
bfsFromFirst.next().value.value; // 1
bfsFromFirst.next().value.value; // 2
bfsFromFirst.next().value.value; // 3
bfsFromFirst.next().value.value; // 4
// ...
```

You can find more illustrstions of usage in the test cases. Let’s move on to the DFS!

Depth-first search is another way to navigate a graph from an initial vertex by recursively the first adjacent node of each vertex found.

The iterative implementation of a DFS is identical to the BFS, but instead of using a `Queue`

you use a `Stack`

:

```
*dfs(first) {
const visited = new Map();
const visitList = new Stack();
visitList.add(first);
while(!visitList.isEmpty()) {
const node = visitList.remove();
if(node && !visited.has(node)) {
yield node;
visited.set(node);
node.getAdjacents().forEach(adj => visitList.add(adj));
}
}
}
```

We can test our graph as follow.

```
const graph = new Graph(Graph.UNDIRECTED);
const [first] = graph.addEdge(1, 2);
graph.addEdge(1, 3);
graph.addEdge(1, 4);
graph.addEdge(5, 2);
graph.addEdge(6, 3);
graph.addEdge(7, 3);
graph.addEdge(8, 4);
graph.addEdge(9, 5);
graph.addEdge(10, 6);
dfsFromFirst = graph.dfs(first);
visitedOrder = Array.from(dfsFromFirst);
const values = visitedOrder.map(node => node.value);
console.log(values); // [1, 4, 8, 3, 7, 6, 10, 2, 5, 9]
```

As you can see the graph is the same on BFS and DFS, however, the order how the nodes were visited is very different. BFS went from 1 to 10 in that order, while DFS went as deep as it could on each node.

We have seen some of the basic operations of a Graph. How to add and remove vertices and edges. Here’s a summary of what we have covered so far:

As you can see, an adjacency list is faster in almost all operations. The only action that the adjacency matrix will outperform the adjacency list is checking if a node is adjacent to other. However, if we change our implementation from Array to a HashSet, we can get it in constant time as well :)

As we saw, Graphs can help to model many real-life scenarios such as airports, social networks, internet and so on. We covered some of the most basic algorithms such as Breadth-First Search (BFS) and Depth-First Search (DFS). Also, we studied about implementations trade-offs such as adjacency list and matrix. Subscribe to my newsletter and don’t miss any of my posts, because there are many other applications that we are going to learn soon, such as finding the shortest path between nodes and different exciting graph algorithms!

#javascript #graphql

1626754433

Thank you very much, it is a very complete article!

1620466520

If you accumulate data on which you base your decision-making as an organization, you should probably think about your data architecture and possible best practices.

If you accumulate data on which you base your decision-making as an organization, you most probably need to think about your data architecture and consider possible best practices. Gaining a competitive edge, remaining customer-centric to the greatest extent possible, and streamlining processes to get on-the-button outcomes can all be traced back to an organization’s capacity to build a future-ready data architecture.

In what follows, we offer a short overview of the overarching capabilities of data architecture. These include user-centricity, elasticity, robustness, and the capacity to ensure the seamless flow of data at all times. Added to these are automation enablement, plus security and data governance considerations. These points from our checklist for what we perceive to be an anticipatory analytics ecosystem.

#big data #data science #big data analytics #data analysis #data architecture #data transformation #data platform #data strategy #cloud data platform #data acquisition

1620629020

The opportunities big data offers also come with very real challenges that many organizations are facing today. Often, it’s finding the most cost-effective, scalable way to store and process boundless volumes of data in multiple formats that come from a growing number of sources. Then organizations need the analytical capabilities and flexibility to turn this data into insights that can meet their specific business objectives.

This Refcard dives into how a data lake helps tackle these challenges at both ends — from its enhanced architecture that’s designed for efficient data ingestion, storage, and management to its advanced analytics functionality and performance flexibility. You’ll also explore key benefits and common use cases.

As technology continues to evolve with new data sources, such as IoT sensors and social media churning out large volumes of data, there has never been a better time to discuss the possibilities and challenges of managing such data for varying analytical insights. In this Refcard, we dig deep into how data lakes solve the problem of storing and processing enormous amounts of data. While doing so, we also explore the benefits of data lakes, their use cases, and how they differ from data warehouses (DWHs).

*This is a preview of the Getting Started With Data Lakes Refcard. To read the entire Refcard, please download the PDF from the link above.*

#big data #data analytics #data analysis #business analytics #data warehouse #data storage #data lake #data lake architecture #data lake governance #data lake management

1617959340

Companies across every industry rely on big data to make strategic decisions about their business, which is why data analyst roles are constantly in demand. Even as we transition to more automated data collection systems, data analysts remain a crucial piece in the data puzzle. Not only do they build the systems that extract and organize data, but they also make sense of it –– identifying patterns, trends, and formulating actionable insights.

If you think that an entry-level data analyst role might be right for you, you might be wondering what to focus on in the first 90 days on the job. What skills should you have going in and what should you focus on developing in order to advance in this career path?

Let’s take a look at the most important things you need to know.

#data #data-analytics #data-science #data-analysis #big-data-analytics #data-privacy #data-structures #good-company

1623479798

Hello! Today I’ll be going over *graphs* . These data structures are the most widely used on the web, given the countless forms that a graph can organize values or even the data structures that can be made from them. In fact, the worldwide web itself can be represented as a graph. Let’s jump right into it!

#javascript #graph #algorithms #data-structures

1624298400

This complete 134-part JavaScript tutorial for beginners will teach you everything you need to know to get started with the JavaScript programming language.

⭐️Course Contents⭐️

0:00:00 Introduction

0:01:24 Running JavaScript

0:04:23 Comment Your Code

0:05:56 Declare Variables

0:06:15 Storing Values with the Assignment Operator

0:11:31 Initializing Variables with the Assignment Operator

0:11:58 Uninitialized Variables

0:12:40 Case Sensitivity in Variables

0:14:05 Add Two Numbers

0:14:34 Subtract One Number from Another

0:14:52 Multiply Two Numbers

0:15:12 Dividing Numbers

0:15:30 Increment

0:15:58 Decrement

0:16:22 Decimal Numbers

0:16:48 Multiply Two Decimals

0:17:18 Divide Decimals

0:17:33 Finding a Remainder

0:18:22 Augmented Addition

0:19:22 Augmented Subtraction

0:20:18 Augmented Multiplication

0:20:51 Augmented Division

0:21:19 Declare String Variables

0:22:01 Escaping Literal Quotes

0:23:44 Quoting Strings with Single Quotes

0:25:18 Escape Sequences

0:26:46 Plus Operator

0:27:49 Plus Equals Operator

0:29:01 Constructing Strings with Variables

0:30:14 Appending Variables to Strings

0:31:11 Length of a String

0:32:01 Bracket Notation

0:33:27 Understand String Immutability

0:34:23 Find the Nth Character

0:34:51 Find the Last Character

0:35:48 Find the Nth-to-Last Character

0:36:28 Word Blanks

0:40:44 Arrays

0:41:43 Nest Arrays

0:42:33 Access Array Data

0:43:34 Modify Array Data

0:44:48 Access Multi-Dimensional Arrays

0:46:30 push()

0:47:29 pop()

0:48:33 shift()

0:49:23 unshift()

0:50:36 Shopping List

0:51:41 Write Reusable with Functions

0:53:41 Arguments

0:55:43 Global Scope

0:59:31 Local Scope

1:00:46 Global vs Local Scope in Functions

1:02:40 Return a Value from a Function

1:03:55 Undefined Value returned

1:04:52 Assignment with a Returned Value

1:05:52 Stand in Line

1:08:41 Boolean Values

1:09:24 If Statements

1:11:51 Equality Operator

1:13:18 Strict Equality Operator

1:14:43 Comparing different values

1:15:38 Inequality Operator

1:16:20 Strict Inequality Operator

1:17:05 Greater Than Operator

1:17:39 Greater Than Or Equal To Operator

1:18:09 Less Than Operator

1:18:44 Less Than Or Equal To Operator

1:19:17 And Operator

1:20:41 Or Operator

1:21:37 Else Statements

1:22:27 Else If Statements

1:23:30 Logical Order in If Else Statements

1:24:45 Chaining If Else Statements

1:27:45 Golf Code

1:32:15 Switch Statements

1:35:46 Default Option in Switch Statements

1:37:23 Identical Options in Switch Statements

1:39:20 Replacing If Else Chains with Switch

1:41:11 Returning Boolean Values from Functions

1:42:20 Return Early Pattern for Functions

1:43:38 Counting Cards

1:49:11 Build Objects

1:50:46 Dot Notation

1:51:33 Bracket Notation

1:52:47 Variables

1:53:34 Updating Object Properties

1:54:30 Add New Properties to Object

1:55:19 Delete Properties from Object

1:55:54 Objects for Lookups

1:57:43 Testing Objects for Properties

1:59:15 Manipulating Complex Objects

2:01:00 Nested Objects

2:01:53 Nested Arrays

2:03:06 Record Collection

2:10:15 While Loops

2:11:35 For Loops

2:13:56 Odd Numbers With a For Loop

2:15:28 Count Backwards With a For Loop

2:17:08 Iterate Through an Array with a For Loop

2:19:43 Nesting For Loops

2:22:45 Do…While Loops

2:24:12 Profile Lookup

2:28:18 Random Fractions

2:28:54 Random Whole Numbers

2:30:21 Random Whole Numbers within a Range

2:31:46 parseInt Function

2:32:36 parseInt Function with a Radix

2:33:29 Ternary Operator

2:34:57 Multiple Ternary Operators

2:36:57 var vs let

2:39:02 var vs let scopes

2:41:32 const Keyword

2:43:40 Mutate an Array Declared with const

2:44:52 Prevent Object Mutation

2:47:17 Arrow Functions

2:28:24 Arrow Functions with Parameters

2:49:27 Higher Order Arrow Functions

2:53:04 Default Parameters

2:54:00 Rest Operator

2:55:31 Spread Operator

2:57:18 Destructuring Assignment: Objects

3:00:18 Destructuring Assignment: Nested Objects

3:01:55 Destructuring Assignment: Arrays

3:03:40 Destructuring Assignment with Rest Operator to Reassign Array

3:05:05 Destructuring Assignment to Pass an Object

3:06:39 Template Literals

3:10:43 Simple Fields

3:12:24 Declarative Functions

3:12:56 class Syntax

3:15:11 getters and setters

3:20:25 import vs require

3:22:33 export

3:23:40 * to Import

3:24:50 export default

3:25:26 Import a Default Export

📺 The video in this post was made by freeCodeCamp.org

The origin of the article: https://www.youtube.com/watch?v=PkZNo7MFNFg&list=PLWKjhJtqVAblfum5WiQblKPwIbqYXkDoC&index=4

🔥 If you’re a beginner. I believe the article below will be useful to you ☞ What You Should Know Before Investing in Cryptocurrency - For Beginner

⭐ ⭐ ⭐**The project is of interest to the community. Join to Get free ‘GEEK coin’ (GEEKCASH coin)**!

☞ **-----CLICK HERE-----**⭐ ⭐ ⭐

Thanks for visiting and watching! Please don’t forget to leave a like, comment and share!

#javascript #learn javascript #learn javascript for beginners #learn javascript - full course for beginners #javascript programming language