DEV Community

Cover image for Learn Number of Islands, Invert Binary Tree, and Course Schedule with Step-by-Step Visualization in DSA View View πŸ‘€πŸ‘€
nyaomaru
nyaomaru

Posted on AI-assisted

Learn Number of Islands, Invert Binary Tree, and Course Schedule with Step-by-Step Visualization in DSA View View πŸ‘€πŸ‘€

Hoi hoi!

I’m @nyaomaru, a frontend engineer who is surprised by how cold it is in the Netherlands even though it’s still summer. 😸

Have you used DSA View View already? πŸ‘€πŸ‘€

DSA View View allows you to understand DSA by visualizing how your implementation actually runs.

In the previous articles, we looked at problems like:

  • Two Sum
  • Binary Search
  • Bubble Sort
  • Valid Parentheses
  • Reverse Linked List
  • Maximum Depth of Binary Tree

This time, let's try three more classic problems:

  • Number of Islands
  • Invert Binary Tree
  • Course Schedule

These three problems introduce some very useful ways of thinking:

Explore connected things
Transform a tree with recursion
Resolve dependencies in the right order
Enter fullscreen mode Exit fullscreen mode

The implementations are not huge.

But the runtime can become surprisingly difficult to hold in our heads.

So let's view what actually happens. πŸ‘€

Let's learn together! 😸

Step view 3


🏝️ Number of Islands

Let's start with Number of Islands.

Suppose we have this grid:

1 1 0 0
1 0 0 1
0 0 1 1
0 0 0 0
Enter fullscreen mode Exit fullscreen mode

1 means land. 🏝️

0 means water. 🌊

Land connected vertically or horizontally belongs to the same island.

So how many islands are there?

Let's look at the first group.

1 1
1
Enter fullscreen mode Exit fullscreen mode

These cells are connected.

So they form one island.

On the right side

    1
  1 1
Enter fullscreen mode Exit fullscreen mode

Those cells are also connected.

So the answer is 2.

Nice! 🏝️🏝️

But how do we make the code understand that several 1s belong to the same island?

Find One Land, Then Explore All Connected Land

The basic idea is

When we find a new 1, count one island and visit all land connected to it.

Let's use this implementation.

function numIslands(grid: string[][]): number {
  let islands = 0;

  const visit = (row: number, col: number): void => {
    if (row < 0 || col < 0) return;
    if (row >= grid.length || col >= grid[row].length) return;
    if (grid[row][col] !== "1") return;

    grid[row][col] = "0";
    visit(row + 1, col);
    visit(row - 1, col);
    visit(row, col + 1);
    visit(row, col - 1);
  };

  for (let row = 0; row < grid.length; row++) {
    for (let col = 0; col < grid[row].length; col++) {
      if (grid[row][col] === "1") {
        islands++;
        visit(row, col);
      }
    }
  }

  return islands;
}
Enter fullscreen mode Exit fullscreen mode

There are two important parts.

First, we scan the grid

for (let row = 0; row < grid.length; row++) {
  for (let col = 0; col < grid[row].length; col++) {
Enter fullscreen mode Exit fullscreen mode

Then, when we find land

if (grid[row][col] === "1") {
  islands++;
  visit(row, col);
}
Enter fullscreen mode Exit fullscreen mode

We count a new island.

But then visit() does something important.

It removes all land connected to that island from future consideration.

Why Do We Change 1 to 0?

Inside visit() we have

grid[row][col] = "0";
Enter fullscreen mode Exit fullscreen mode

At first, changing land into water looks a little strange. 😿

But here, 0 really means

We already visited this land.

Let's follow a tiny example.

1 1
1 0
Enter fullscreen mode Exit fullscreen mode

We start at the top-left. And we found land! So

islands = 1
Enter fullscreen mode Exit fullscreen mode

Then

visit(0, 0);
Enter fullscreen mode Exit fullscreen mode

Inside visit(), we mark it as visited.

0 1
1 0
Enter fullscreen mode Exit fullscreen mode

Then we visit the four directions

down
up
right
left
Enter fullscreen mode Exit fullscreen mode

Going down finds another 1.

0 1
1 0
↑
Enter fullscreen mode Exit fullscreen mode

So we visit it too.

0 1
0 0
Enter fullscreen mode Exit fullscreen mode

Going right from the original cell also finds land.

Visit it.

0 0
0 0
Enter fullscreen mode Exit fullscreen mode

Now the whole connected island has disappeared from our search.

When the outer loops continue, there is no 1 left in that island to count again.

That's the key idea.

Count once, then mark the whole connected area as visited.

Why Four Recursive Calls?

We use

visit(row + 1, col);
visit(row - 1, col);
visit(row, col + 1);
visit(row, col - 1);
Enter fullscreen mode Exit fullscreen mode

That means

        up
         ↑
left ← current β†’ right
         ↓
        down
Enter fullscreen mode Exit fullscreen mode

Each visited cell asks

Is there more land next to me?

And every newly discovered land cell asks the same question again.

This continues until we reach:

  • water
  • outside the grid
  • land we already visited

Those cases stop the recursion.

The Base Cases

These lines protect us

if (row < 0 || col < 0) return;
if (row >= grid.length || col >= grid[row].length) return;
if (grid[row][col] !== "1") return;
Enter fullscreen mode Exit fullscreen mode

So,

  • If we walk outside the grid, stop.
  • If we reach water, stop.
  • If we reach a cell we already changed to 0, stop.

Otherwise, continue exploring.

Let's Follow Two Islands

Consider

1 1 0
0 0 1
0 1 1
Enter fullscreen mode Exit fullscreen mode

The scan begins at the top-left.

1 1 0
↑
0 0 1
0 1 1
Enter fullscreen mode Exit fullscreen mode

Found land.

islands = 1
Enter fullscreen mode Exit fullscreen mode

visit() removes everything connected to it.

0 0 0
0 0 1
0 1 1
Enter fullscreen mode Exit fullscreen mode

The loops continue.

Eventually we reach

0 0 0
0 0 1
    ↑
0 1 1
Enter fullscreen mode Exit fullscreen mode

Another 1.

So

islands = 2
Enter fullscreen mode Exit fullscreen mode

And visit() explores that entire connected area.

0 0 0
0 0 0
0 0 0
Enter fullscreen mode Exit fullscreen mode

Done!

2 islands
Enter fullscreen mode Exit fullscreen mode

πŸŽ‰

Complexity

Every cell is processed at most a small number of times.

If the grid has m rows and n columns

Time:  O(m Γ— n)
Enter fullscreen mode Exit fullscreen mode

In the worst case, the recursive call stack may grow with the number of land cells.

Space: O(m Γ— n)
Enter fullscreen mode Exit fullscreen mode

πŸ‘€ Let's View View It

This is one of those algorithms where the final code is pretty small.

But while reading it, there are a lot of things moving at once

row
col
grid
islands
recursive calls
Enter fullscreen mode Exit fullscreen mode

And then suddenly we see

grid[row][col] = "0";
Enter fullscreen mode Exit fullscreen mode
  • Why did that cell disappear?
  • Which recursive call are we inside?
  • Which cells belong to the current island?
  • Where will the outer loop continue after recursion finishes?

That is a lot to simulate mentally. 😿

Number Of Islands

When we view it step by step, the idea becomes much more visual.

Find land
  ↓
islands++
  ↓
visit connected land
  ↓
mark it visited
  ↓
expand up / down / left / right
  ↓
return to scanning
  ↓
find next island
Enter fullscreen mode Exit fullscreen mode

Instead of thinking about recursion first, I like to think about it like this

Find one island, paint the whole island away, then keep searching.

🏝️😸


🌳 Invert Binary Tree

Next, let's invert a binary tree.

Suppose we have

        4
       / \
      2   7
     / \ / \
    1  3 6  9
Enter fullscreen mode Exit fullscreen mode

We want to change

        4
       / \
      7   2
     / \ / \
    9  6 3  1
Enter fullscreen mode Exit fullscreen mode

Every left child becomes the right child.

Every right child becomes the left child.

Simple, right?

Well, the final implementation is also surprisingly small.

function invertTree(root: TreeNode | null): TreeNode | null {
  if (root === null) return null;

  const left = invertTree(root.left);
  const right = invertTree(root.right);

  root.left = right;
  root.right = left;

  return root;
}
Enter fullscreen mode Exit fullscreen mode

That's almost suspiciously short. πŸ‘€

Go Down First

Let's use a smaller tree.

    1
   / \
  2   3
Enter fullscreen mode Exit fullscreen mode

We start at node 1.

But we don't swap immediately.

First,

const left = invertTree(root.left);
Enter fullscreen mode Exit fullscreen mode

So we go to node 2.

Node 2 also tries to invert its left child.

But there is no child.

So

if (root === null) return null;
Enter fullscreen mode Exit fullscreen mode

returns null.

The same thing happens on the right side of node 2.

Now node 2 has

left = null
right = null
Enter fullscreen mode Exit fullscreen mode

So

root.left = right;
root.right = left;
Enter fullscreen mode Exit fullscreen mode

does not visibly change anything.

Node 2 returns.

Then node 1 explores its right subtree.

3
Enter fullscreen mode Exit fullscreen mode

Node 3 also has no children, so it returns after the same process.

Only then do we come back to node 1.

Now

left = 2
right = 3
Enter fullscreen mode Exit fullscreen mode

And we do

root.left = right;
root.right = left;
Enter fullscreen mode Exit fullscreen mode

So

    1
   / \
  2   3
Enter fullscreen mode Exit fullscreen mode

becomes πŸ‘‡

    1
   / \
  3   2
Enter fullscreen mode Exit fullscreen mode

Done! πŸŽ‰

The Important Part: The Swap Happens on the Way Back

This is what makes the recursive solution interesting.

The function first goes down.

1
↓
2
↓
null
Enter fullscreen mode Exit fullscreen mode

Then it comes back.

Later it explores the other side.

1
↓
3
↓
null
Enter fullscreen mode Exit fullscreen mode

And after both children have returned, the current node swaps them.

So the flow is more like

Go left
  ↓
Invert left subtree
  ↓
Go right
  ↓
Invert right subtree
  ↓
Swap the returned subtrees
  ↓
Return current node
Enter fullscreen mode Exit fullscreen mode

The tree transformation is built while recursion unwinds.

A Slightly Bigger Example

Let's look at

      4
     / \
    2   7
   / \
  1   3
Enter fullscreen mode Exit fullscreen mode

We start at 4.

invertTree(4)
Enter fullscreen mode Exit fullscreen mode

Then

invertTree(2)
Enter fullscreen mode Exit fullscreen mode

Then

invertTree(1)
Enter fullscreen mode Exit fullscreen mode

Node 1 returns.

Then node 3 returns.

Now node 2 has

left = 1
right = 3
Enter fullscreen mode Exit fullscreen mode

Swap them.

    2
   / \
  3   1
Enter fullscreen mode Exit fullscreen mode

Then recursion returns to 4.

The right subtree rooted at 7 is also processed.

Finally node 4 receives

left = inverted subtree rooted at 2
right = inverted subtree rooted at 7
Enter fullscreen mode Exit fullscreen mode

and swaps them.

The final tree becomes

      4
     / \
    7   2
       / \
      3   1
Enter fullscreen mode Exit fullscreen mode

The interesting thing is that each node only needs to know about its own two children.

It doesn't need to understand the whole tree.

Complexity

We visit every node once.

Time: O(n)
Enter fullscreen mode Exit fullscreen mode

The recursive call stack depends on the height of the tree.

Space: O(h)
Enter fullscreen mode Exit fullscreen mode

For a balanced tree

O(log n)
Enter fullscreen mode Exit fullscreen mode

In the worst case

O(n)
Enter fullscreen mode Exit fullscreen mode

πŸ‘€ Let's View View It

This is exactly where recursion can become difficult to mentally simulate.

The code says

const left = invertTree(root.left);
const right = invertTree(root.right);
Enter fullscreen mode Exit fullscreen mode

Then

root.left = right;
root.right = left;
Enter fullscreen mode Exit fullscreen mode

But my brain immediately starts asking:

  • Which root are we talking about now?
  • Did node 2 already swap?
  • Are we still going down?
  • Or are we coming back up?
  • What does left contain at this moment?

😿

Invert Tree

When we step through the runtime, we can separate two different movements.

The final code is short.

But the actual runtime has a rhythm 🎡

Go down
↓
Return
↓
Swap
↓
Return
↓
Swap
Enter fullscreen mode Exit fullscreen mode

Once I can see that rhythm, the recursive solution feels much less magical. 🌳😸


πŸŽ“ Course Schedule

Finally, let's look at Course Schedule.

This one is a little more difficult.

Suppose we have three courses

0
1
2
Enter fullscreen mode Exit fullscreen mode

And the prerequisites are

[1, 0]
[2, 1]
Enter fullscreen mode Exit fullscreen mode

That means

To take course 1, finish course 0 first.
To take course 2, finish course 1 first.
Enter fullscreen mode Exit fullscreen mode

So the dependency looks like

0 β†’ 1 β†’ 2
Enter fullscreen mode Exit fullscreen mode

Can we finish all courses?

Yes.

We can take them in this order 0 β†’ 1 β†’ 2.

Easy.

But what if the dependencies look like this?

0 β†’ 1
↑   ↓
└── 2
Enter fullscreen mode Exit fullscreen mode

Now

0 needs 2
1 needs 0
2 needs 1
Enter fullscreen mode Exit fullscreen mode

Everyone is waiting for someone else.

We can never start.

That is a cycle.

And if there is a cycle, we cannot finish all courses.

Build the Graph

Here is the implementation:

function canFinish(numCourses: number, prerequisites: number[][]): boolean {
  const graph: number[][] = Array.from({ length: numCourses }, () => []);
  const indegree: number[] = Array(numCourses).fill(0);

  for (const [course, prerequisite] of prerequisites) {
    graph[prerequisite].push(course);
    indegree[course]++;
  }

  const queue: number[] = [];
  for (let course = 0; course < numCourses; course++) {
    if (indegree[course] === 0) queue.push(course);
  }

  let completed = 0;
  for (let head = 0; head < queue.length; head++) {
    const course = queue[head];
    completed++;

    for (const next of graph[course]) {
      indegree[next]--;
      if (indegree[next] === 0) queue.push(next);
    }
  }

  return completed === numCourses;
}
Enter fullscreen mode Exit fullscreen mode

There are a few moving parts here.

graph
indegree
queue
completed
Enter fullscreen mode Exit fullscreen mode

This is exactly the kind of algorithm where every individual line makes sense.

but the whole thing can still feel confusing. 😿

Let's break it down.

What Is graph?

For

0 β†’ 1 β†’ 2
Enter fullscreen mode Exit fullscreen mode

we want to know

After I finish this course, which courses become closer to being available?

So

graph[0] = [1]
graph[1] = [2]
graph[2] = []
Enter fullscreen mode Exit fullscreen mode

That means

Finish 0
↓
course 1 is affected

Finish 1
↓
course 2 is affected
Enter fullscreen mode Exit fullscreen mode

We build that here

graph[prerequisite].push(course);
Enter fullscreen mode Exit fullscreen mode

What Is indegree?

indegree tells us how many prerequisites a course is still waiting for.

For

0 β†’ 1 β†’ 2
Enter fullscreen mode Exit fullscreen mode

we have

course 0: 0 prerequisites
course 1: 1 prerequisite
course 2: 1 prerequisite
Enter fullscreen mode Exit fullscreen mode

So

indegree = [0, 1, 1]
Enter fullscreen mode Exit fullscreen mode

Course 0 is special. Because it does not need anything before it.

So we can start there immediately.

Start With Courses That Need Nothing

We build the queue

for (let course = 0; course < numCourses; course++) {
  if (indegree[course] === 0) queue.push(course);
}
Enter fullscreen mode Exit fullscreen mode

For our example

indegree = [0, 1, 1]
Enter fullscreen mode Exit fullscreen mode

Only course 0 has zero prerequisites.

So

queue = [0]
Enter fullscreen mode Exit fullscreen mode

This means

Course 0 is currently available.

Finish Course 0

Take

course = 0
Enter fullscreen mode Exit fullscreen mode

Then

completed++;
Enter fullscreen mode Exit fullscreen mode

So

completed = 1
Enter fullscreen mode Exit fullscreen mode

Now look at courses depending on 0.

graph[0] = [1]
Enter fullscreen mode Exit fullscreen mode

Course 1 was waiting for one prerequisite.

But course 0 is now complete.

So

indegree[1]--;
Enter fullscreen mode Exit fullscreen mode

Then

indegree[1] = 0
Enter fullscreen mode Exit fullscreen mode

Now course 1 needs nothing.

So we add it to the queue.

queue = [0, 1]
Enter fullscreen mode Exit fullscreen mode

Finish Course 1

Next

course = 1
Enter fullscreen mode Exit fullscreen mode

Now

completed = 2
Enter fullscreen mode Exit fullscreen mode

Course 2 depends on 1.

So

indegree[2]: 1 β†’ 0
Enter fullscreen mode Exit fullscreen mode

Add it to the queue.

queue = [0, 1, 2]
Enter fullscreen mode Exit fullscreen mode

Finish Course 2

Finally

course = 2
Enter fullscreen mode Exit fullscreen mode

So

completed = 3
Enter fullscreen mode Exit fullscreen mode

And

numCourses = 3
Enter fullscreen mode Exit fullscreen mode

Therefore

completed === numCourses; // true
Enter fullscreen mode Exit fullscreen mode

We can finish everything! πŸŽ‰

Why Does This Detect a Cycle?

Now let's try

0 β†’ 1
↑   ↓
└── 2
Enter fullscreen mode Exit fullscreen mode

Every course has one prerequisite.

So

indegree = [1, 1, 1]
Enter fullscreen mode Exit fullscreen mode

We try to build the initial queue.

if (indegree[course] === 0)
Enter fullscreen mode Exit fullscreen mode

But there is no course with indegree 0.

So, nothing can start.

Therefore

completed = 0
Enter fullscreen mode Exit fullscreen mode

And,

0 === 3 // false
Enter fullscreen mode Exit fullscreen mode

We cannot finish the courses.

Another Example

Suppose

0 β†’ 2
1 β†’ 2
2 β†’ 3
Enter fullscreen mode Exit fullscreen mode

Course 2 needs both 0 and 1.

So

indegree = [0, 0, 2, 1]
Enter fullscreen mode Exit fullscreen mode

The initial queue is

queue = [0, 1]
Enter fullscreen mode Exit fullscreen mode

Finish 0.

indegree[2]: 2 β†’ 1
Enter fullscreen mode Exit fullscreen mode

Course 2 is still waiting.

So don't add it yet.

Finish 1.

indegree[2]: 1 β†’ 0
Enter fullscreen mode Exit fullscreen mode

Now course 2 is ready.

queue = [0, 1, 2]
Enter fullscreen mode Exit fullscreen mode

Finish 2.

indegree[3]: 1 β†’ 0
Enter fullscreen mode Exit fullscreen mode

Now

queue = [0, 1, 2, 3]
Enter fullscreen mode Exit fullscreen mode

Everything can be completed.

This is the key idea

When all prerequisites for a course are resolved, that course becomes available.

Why Use head Instead of shift()?

The queue is processed like this

for (let head = 0; head < queue.length; head++) {
  const course = queue[head]
Enter fullscreen mode Exit fullscreen mode

Instead of repeatedly doing

queue.shift();
Enter fullscreen mode Exit fullscreen mode

we keep an index pointing to the next item to process.

So the queue can grow while we iterate through it.

For example

queue = [0]

process 0
↓
queue = [0, 1]

process 1
↓
queue = [0, 1, 2]
Enter fullscreen mode Exit fullscreen mode

head simply moves forward.

0 β†’ 1 β†’ 2
↑
head
Enter fullscreen mode Exit fullscreen mode

Then

0 β†’ 1 β†’ 2
    ↑
   head
Enter fullscreen mode Exit fullscreen mode

Then

0 β†’ 1 β†’ 2
        ↑
       head
Enter fullscreen mode Exit fullscreen mode

Complexity

V = number of courses
E = number of prerequisite relationships
Enter fullscreen mode Exit fullscreen mode

We build the graph once and process every course and edge.

Time: O(V + E)
Space: O(V + E)
Enter fullscreen mode Exit fullscreen mode

πŸ‘€ Let's View View It

This is probably the most interesting visualization of the three.

Because there are several things changing together.

graph
indegree
queue
head
completed
Enter fullscreen mode Exit fullscreen mode

If I only read

indegree[next]--;
if (indegree[next] === 0) queue.push(next);
Enter fullscreen mode Exit fullscreen mode

I understand the syntax.

But I may still ask:

  • Why did this course become available now?
  • Which prerequisite was removed?
  • Why is this course still not in the queue?
  • What does completed tell us?
  • Where exactly does the cycle get stuck?

When we view the runtime, we can watch the dependencies disappear.

0 β†’ 1 β†’ 2

indegree = [0, 1, 1]
queue = [0]

       ↓ finish 0

indegree = [0, 0, 1]
queue = [0, 1]

       ↓ finish 1

indegree = [0, 0, 0]
queue = [0, 1, 2]

       ↓ finish 2

completed = 3
Enter fullscreen mode Exit fullscreen mode

The code stops feeling like mysterious bookkeeping.

We're really doing one simple thing:

Keep taking courses that are ready, and make their dependent courses closer to being ready.

If eventually every course becomes ready

completed === numCourses
Enter fullscreen mode Exit fullscreen mode

there is no blocking cycle.

If some courses never become ready

completed < numCourses
Enter fullscreen mode Exit fullscreen mode

something is stuck in a cycle. πŸŽ“πŸ˜Έ


🧠 What Did We Actually Learn?

These three problems look very different.

But each one teaches a useful way of thinking.

Number of Islands

When you find one part of a connected group, explore the entire group before continuing.

What else is connected to this?
Enter fullscreen mode Exit fullscreen mode

Invert Binary Tree

Let recursive calls solve the smaller subtrees first, then transform the current node using their results.

Can my children finish their work before I change this node?
Enter fullscreen mode Exit fullscreen mode

Course Schedule

Process things that have no unresolved dependencies, then use them to unlock more work.

What can I safely process right now?
Enter fullscreen mode Exit fullscreen mode

The implementations are not very long.

But each one introduces a different mental model.

DFS on a grid
Recursive tree transformation
Topological sorting
Enter fullscreen mode Exit fullscreen mode

And once again, the syntax is not really the hardest part.

The difficult part is keeping track of the changing state.

  • Where are we?
  • What changed?
  • What is waiting?
  • What has already been visited?
  • Which recursive call are we inside?

Sometimes I can read every line and still lose the thread somewhere in the middle. 😿

That's exactly when I want to view it. πŸ‘€πŸ‘€


🎯 Conclusion

In this article, we looked at:

  • Number of Islands with recursive grid traversal
  • Invert Binary Tree with recursion
  • Course Schedule with topological sorting

And more importantly, we followed what changed while each algorithm was running.

For Number of Islands, we watched connected land disappear as it became visited.

1 β†’ 0
Enter fullscreen mode Exit fullscreen mode

For Invert Binary Tree, we watched recursive calls go down and the tree change while they returned.

go down
↓
come back
↓
swap
Enter fullscreen mode Exit fullscreen mode

For Course Schedule, we watched prerequisites disappear and new courses enter the queue.

indegree--
↓
0 prerequisites
↓
queue.push()
Enter fullscreen mode Exit fullscreen mode

This is exactly the kind of thing I built DSA View View for.

You can write or load a TypeScript implementation, run it with your own inputs, and move backward and forward through the runtime.

If you are learning DSA too, try viewing one of these problems step by step.

Especially when the implementation looks short, but your brain still says

Wait... what just changed? 😿

Seeing the runtime may make the idea much easier to follow.

And if there is a DSA problem you want me to cover next, please let me know in the comments!

I still have many algorithms to learn myself. 😸

Let's train our DSA muscles together! πŸ’ͺ

If you like DSA View View, please give it a star ⭐

GitHub logo nyaomaru / dsa-view-view

DSA View View allows you to understand DSA to see the data flow. πŸ‘€πŸ‘€ Of course, it's free.

DSA View View

DSA View View

DSA View View turns TypeScript algorithm functions into step-by-step visual stories. πŸ‘€πŸ‘€

DSA View View TV

Write code, run it with structured inputs, and see the arrays, matrices trees, lists, stacks, pointers, and return values move as the function executes.

It is built for those moments when reading the code is not enough and you want to view why the answer changes.

DSA View View demo

Why Try It?

  • 🧠 Step through real TypeScript
    Paste or edit a function, validate it, then run the exact code in the browser.

  • 🧩 Views that match the data
    Arrays become bars, matrices become grids, trees become node graphs, linked lists become chains, and two-pointer area problems get their own visual view.

  • 🌳 DSA-friendly inputs out of the box
    TreeNode, ListNode, MinHeap, MaxHeap, PriorityQueue, nested arrays matrices, strings, numbers, and class-style inputs are supported without ceremony.

  • πŸ”Ž 39 built-in examples
    Search by name, browse…

See you in the next article!

Top comments (5)

Collapse
 
koda2026 profile image
Harun - solo dev

@nyaomaru HOW ARE YOU

Collapse
 
nyaomaru profile image
nyaomaru

Hi! I'm still learning, working, and developing every day. 😸
Especially lately, I've been working on new features in is-kit for TypeScript compiler use cases! πŸš€

Collapse
 
koda2026 profile image
Harun - solo dev

Okay its a long time we spoken ( not taht longπŸ˜…)

Thread Thread
 
nyaomaru profile image
nyaomaru

Of course I remember you! 😸
You’re the genius developer coding on Android! πŸ’ͺ
How have you been?

Collapse
 
technogamerz profile image
π“π‘πž π‹πšπ³π² 𝐆𝐒𝐫π₯

The cover image looks really cool, and the article is truly enjoyable!!