feat(curriculum): add binary search workshop JSV9 (#65964)

Co-authored-by: majestic-owl448 <26656284+majestic-owl448@users.noreply.github.com>
Co-authored-by: Jeevankumar S <jeevenkumar2003@gmail.com>
Co-authored-by: Kolade <chrisjay967@gmail.com>
This commit is contained in:
Zaira
2026-03-27 19:15:35 +05:00
committed by GitHub
parent 72f5d67f6a
commit 431c77820e
19 changed files with 890 additions and 0 deletions

View File

@@ -5645,6 +5645,13 @@
"These lessons cover algorithm implementations, time and space complexity analysis, and the divide and conquer programming paradigm."
]
},
"workshop-binary-search-js": {
"title": "Implement the Binary Search Algorithm",
"intro": [
"The binary search algorithm is a searching algorithm used to find a target item in a sorted list.",
"In this workshop, you'll implement the binary search algorithm and return the path it took to find the target or return 'Value not found'."
]
},
"lab-bubble-sort-algorithm": {
"title": "Implement the Bubble Sort Algorithm",
"intro": [

View File

@@ -0,0 +1,44 @@
---
id: 6995d0325fd7da40854fac40
title: Step 1
challengeType: 1
dashedName: step-1
---
# --description--
In a previous lesson, you learned about binary search and how it differs from linear search.
In this workshop, you will put that into practice by building a binary search algorithm.
To begin, create a `binarySearch` function with a `searchList` and `value` parameters.
`searchList` is the array of elements the function will search within, and `value` is the target the function will try to find within the `searchList`.
# --hints--
You should create a `binarySearch` function.
```js
assert.isFunction(binarySearch);
```
Your `binarySearch` function should have a `searchList` and `value` parameters.
```js
const binSearchParams = __helpers.getFunctionParams(binarySearch.toString());
const binarySearchParamsNames = binSearchParams.map(param => param.name)
assert.include(binarySearchParamsNames, "searchList");
assert.include(binarySearchParamsNames, "value");
```
# --seed--
## --seed-contents--
```js
--fcc-editable-region--
--fcc-editable-region--
```

View File

@@ -0,0 +1,33 @@
---
id: 6996c60281feca6718cc15c1
title: Step 2
challengeType: 1
dashedName: step-2
---
# --description--
Inside your `binarySearch` function, create a `pathToTarget` variable and initialize it to an empty array, for a start.
# --hints--
You should have a `pathToTarget` variable set to an empty array `[]` within your `binarySearch` function.
```js
assert.match(
__helpers.removeJSComments(String(binarySearch)),
/(const|let|var)\s+pathToTarget\s*=\s*\[\s*\]/
);
```
# --seed--
## --seed-contents--
```js
function binarySearch(searchList, value) {
--fcc-editable-region--
--fcc-editable-region--
}
```

View File

@@ -0,0 +1,44 @@
---
id: 6996c675b599baa7b103192d
title: Step 3
challengeType: 1
dashedName: step-3
---
# --description--
In binary search, there is the lowest possible index and the highest possible index.
The lowest possible index represents the leftmost boundary in the current portion of the values being searched within, in this case, `searchList`. The highest possible index, on the other hand, represents the rightmost boundary in the values being searched.
To account for those two, you have to consider the entire array being searched. So, inside the `binarySearch` function, define a `low` variable with a value of `0`, and a `high` variable with a value that accounts for the last index in the array being searched.
# --hints--
You should have a variable named `low` set to `0` in your `binarySearch` function.
```js
assert.match(__helpers.removeJSComments(String(binarySearch)), /(const|let|var)\s+low\s*=\s*0/);
```
You should have a variable named `high` set to `searchList.length - 1` in your `binarySearch` function.
```js
assert.match(
__helpers.removeJSComments(String(binarySearch)),
/(const|let|var)\s+high\s*=\s*searchList\.length\s*-\s*1/
);
```
# --seed--
## --seed-contents--
```js
function binarySearch(searchList, value) {
let pathToTarget = [];
--fcc-editable-region--
--fcc-editable-region--
}
```

View File

@@ -0,0 +1,41 @@
---
id: 6996c7363a4a437839f1f15d
title: Step 4
challengeType: 1
dashedName: step-4
---
# --description--
Now that you've defined the boundaries of your search, it's time to create the loop that will perform the binary search algorithm.
Binary search works by repeatedly narrowing down the search space. This process continues as long as there is a valid range of elements to check.
You can express this with a `while` loop that will continue as long as your `low` pointer is less than or equal to your `high` pointer.
If `low` ever becomes greater than `high`, it means the search space has become empty, and the `value` is not in the array.
Inside the `binarySearch` function, create a `while` loop with a condition that checks if `low` is less than or equal to high.
# --hints--
You should create a `while` loop that runs while `low` is less than or equal to `high`.
```js
assert.match(__helpers.removeJSComments(String(binarySearch)), /while\s*\(\s*low\s*<=\s*high\s*\)/);
```
# --seed--
## --seed-contents--
```js
function binarySearch(searchList, value) {
let pathToTarget = [];
let low = 0;
let high = searchList.length - 1;
--fcc-editable-region--
--fcc-editable-region--
}
```

View File

@@ -0,0 +1,42 @@
---
id: 6996c796385b6ea4e222d5e8
title: Step 5
challengeType: 1
dashedName: step-5
---
# --description--
To get started with the loop, you need to find the middle index of the current search space.
You can get the middle index by calculating the average of the `low` and `high` variables. You will have to use floor division to get the average after adding the two values, so the answer would always be an integer rounded down, as the indices must be integers.
Within your `while` loop, create a `mid` variable set to the average of `low` and `high`.
# --hints--
Within the while loop you should create a `mid` variable set to `Math.floor((low + high) / 2)`.
```js
assert.match(
__helpers.removeJSComments(String(binarySearch)),
/(const|let|var)\s+mid\s*=\s*Math\.floor\(\s*\(\s*low\s*\+\s*high\s*\)\s*\/\s*2\s*\)/
);
```
# --seed--
## --seed-contents--
```js
function binarySearch(searchList, value) {
let pathToTarget = [];
let low = 0;
let high = searchList.length - 1;
while (low <= high) {
--fcc-editable-region--
--fcc-editable-region--
}
}
```

View File

@@ -0,0 +1,41 @@
---
id: 6996c8375f4301004459ee85
title: Step 6
challengeType: 1
dashedName: step-6
---
# --description--
Now that you've calculated the `mid` index, you need to actually retrieve the value from the `searchList` at that index. This value is what you will compare against your `value` parameter, which is the target you are searching for.
Create a `valueAtMiddle` variable and assign it the element from `searchList` that is located at the `mid` index.
# --hints--
You should create a `valueAtMiddle` variable set to `searchList[mid]`.
```js
assert.match(
__helpers.removeJSComments(String(binarySearch)),
/(const|let|var)\s+valueAtMiddle\s*=\s*searchList\s*\[\s*mid\s*\]/
);
```
# --seed--
## --seed-contents--
```js
function binarySearch(searchList, value) {
let pathToTarget = [];
let low = 0;
let high = searchList.length - 1;
while (low <= high) {
let mid = Math.floor((low + high) / 2);
--fcc-editable-region--
--fcc-editable-region--
}
}
```

View File

@@ -0,0 +1,40 @@
---
id: 6996c8f94fe6b250f11a2a1e
title: Step 7
challengeType: 1
dashedName: step-7
---
# --description--
Now, include `valueAtMiddle` in the `pathToTarget` array to track the steps taken during the search, regardless of whether it's the target. You can do this with the `push()` method.
# --hints--
You should use the `push()` method to add `valueAtMiddle` to the `pathToTarget` array.
```js
assert.match(
__helpers.removeJSComments(String(binarySearch)),
/pathToTarget\s*\.\s*push\(\s*valueAtMiddle\s*\)/
);
```
# --seed--
## --seed-contents--
```js
function binarySearch(searchList, value) {
let pathToTarget = [];
let low = 0;
let high = searchList.length - 1;
while (low <= high) {
let mid = Math.floor((low + high) / 2);
let valueAtMiddle = searchList[mid];
--fcc-editable-region--
--fcc-editable-region--
}
}
```

View File

@@ -0,0 +1,52 @@
---
id: 6996c95e5bede03bdf2937a2
title: Step 8
challengeType: 1
dashedName: step-8
---
# --description--
The next thing to do is to create a condition that will check if the target `value` is in the middle.
Inside the `while` loop, create an `if` statement that checks if the target `value` is equal to `valueAtMiddle`. If it is, return the `pathToTarget` variable.
# --hints--
You should create an `if` statement with the condition `value === valueAtMiddle`.
```js
assert.match(
__helpers.removeJSComments(String(binarySearch)),
/if\s*\(\s*value\s*===?\s*valueAtMiddle\s*\)/
);
```
You should return the `pathToTarget` variable inside the `if` statement.
```js
assert.match(
__helpers.removeJSComments(String(binarySearch)),
/if\s*\(\s*value\s*===?\s*valueAtMiddle\s*\)\s*\{[\s\S]*?return\s+pathToTarget/
);
```
# --seed--
## --seed-contents--
```js
function binarySearch(searchList, value) {
let pathToTarget = [];
let low = 0;
let high = searchList.length - 1;
while (low <= high) {
let mid = Math.floor((low + high) / 2);
let valueAtMiddle = searchList[mid];
pathToTarget.push(valueAtMiddle);
--fcc-editable-region--
--fcc-editable-region--
}
}
```

View File

@@ -0,0 +1,55 @@
---
id: 6996ca21a74104721f2c7e5d
title: Step 9
challengeType: 1
dashedName: step-9
---
# --description--
You need to test out things so you will understand the flow of the algorithm at this initial stage.
To do that, you need to first break out of the loop. That's because the current implementation will only allow one iteration, so if the condition is not met, there will be an infinite loop.
Just after the `if` statement, use the `break` keyword to break out of the `while` loop. Then, after the `while` loop, return an empty array to signify that the value was not found.
# --hints--
You should use the `break` keyword to break out of the `while` loop after the `if` statement.
```js
assert.match(
__helpers.removeJSComments(String(binarySearch)),
/if\s*\(\s*value\s*===?\s*valueAtMiddle\s*\)\s*\{[\s\S]*?\}\s*break\s*;?/
);
```
You should return an empty array after your `while` loop.
```js
assert.match(__helpers.removeJSComments(String(binarySearch)), /return\s*\[\s*\]/);
```
# --seed--
## --seed-contents--
```js
function binarySearch(searchList, value) {
let pathToTarget = [];
let low = 0;
let high = searchList.length - 1;
while (low <= high) {
let mid = Math.floor((low + high) / 2);
let valueAtMiddle = searchList[mid];
pathToTarget.push(valueAtMiddle);
if (value === valueAtMiddle) {
return pathToTarget;
}
--fcc-editable-region--
}
--fcc-editable-region--
}
```

View File

@@ -0,0 +1,58 @@
---
id: 6996cb5869acf58de751ae81
title: Step 10
challengeType: 1
dashedName: step-10
---
# --description--
Recall that the function takes an array called `searchList` and the `value` you're looking for.
Now, call the function with `binarySearch([1, 2, 3, 4, 5], 3)` and `binarySearch([1, 2, 3, 4, 5, 9], 4)` and log both statements to the console.
# --hints--
You should call the function with `binarySearch([1, 2, 3, 4, 5], 3)` and log it to the console.
```js
assert.match(
__helpers.removeJSComments(code),
/console\s*\.\s*log\s*\(\s*binarySearch\s*\(\s*\[\s*1\s*,\s*2\s*,\s*3\s*,\s*4\s*,\s*5\s*\]\s*,\s*3\s*\)\s*\)/
);
```
You should call the function with `binarySearch([1, 2, 3, 4, 5, 9], 4)` and log it to the console.
```js
assert.match(
__helpers.removeJSComments(code),
/console\s*\.\s*log\s*\(\s*binarySearch\s*\(\s*\[\s*1\s*,\s*2\s*,\s*3\s*,\s*4\s*,\s*5\s*,\s*9\s*\]\s*,\s*4\s*\)\s*\)/
);
```
# --seed--
## --seed-contents--
```js
function binarySearch(searchList, value) {
let pathToTarget = [];
let low = 0;
let high = searchList.length - 1;
while (low <= high) {
let mid = Math.floor((low + high) / 2);
let valueAtMiddle = searchList[mid];
pathToTarget.push(valueAtMiddle);
if (value === valueAtMiddle) {
return pathToTarget;
}
break;
}
return [];
}
--fcc-editable-region--
--fcc-editable-region--
```

View File

@@ -0,0 +1,52 @@
---
id: 6996ccadf9048c79b857bb15
title: Step 11
challengeType: 1
dashedName: step-11
---
# --description--
You can see that the first call returns `[3]`. That's because `3` is the middle value in `[1, 2, 3, 4, 5]`.
On the other hand, the second call returns an empty array. This happens because, after checking the middle value, the loop currently lacks the logic to narrow the search range by updating the `low` or `high` variables. It only acts as a single midpoint check and then finishes without further searching.
To allow the binary search to continue narrowing its search, add an `else if` statement that checks if `value` is greater than `valueAtMiddle`.
# --hints--
You should add an `else if` statement with the condition `value > valueAtMiddle`.
```js
assert.match(
__helpers.removeJSComments(String(binarySearch)),
/else\s+if\s*\(\s*value\s*>\s*valueAtMiddle\s*\)/
);
```
# --seed--
## --seed-contents--
```js
function binarySearch(searchList, value) {
let pathToTarget = [];
let low = 0;
let high = searchList.length - 1;
while (low <= high) {
let mid = Math.floor((low + high) / 2);
let valueAtMiddle = searchList[mid];
pathToTarget.push(valueAtMiddle);
--fcc-editable-region--
if (value === valueAtMiddle) {
return pathToTarget;
}
--fcc-editable-region--
break;
}
return [];
}
console.log(binarySearch([1, 2, 3, 4, 5], 3));
console.log(binarySearch([1, 2, 3, 4, 5, 9], 4));
```

View File

@@ -0,0 +1,51 @@
---
id: 6996cdf1f60cbc30eeeae1c9
title: Step 12
challengeType: 1
dashedName: step-12
---
# --description--
If the condition in the `else if` is true, update the value of the `low` variable by adding `1` to the `mid` variable.
This will extend the search to the right half of the current search areas in the list, because if the `value` is greater than `valueAtMiddle`, it means the `value` must be in the right half of the current search area.
# --hints--
You should update the `low` variable to `mid + 1`.
```js
assert.match(__helpers.removeJSComments(String(binarySearch)), /low\s*=\s*mid\s*\+\s*1/);
```
# --seed--
## --seed-contents--
```js
function binarySearch(searchList, value) {
let pathToTarget = [];
let low = 0;
let high = searchList.length - 1;
while (low <= high) {
let mid = Math.floor((low + high) / 2);
let valueAtMiddle = searchList[mid];
pathToTarget.push(valueAtMiddle);
if (value === valueAtMiddle) {
return pathToTarget;
} else if (value > valueAtMiddle) {
--fcc-editable-region--
--fcc-editable-region--
}
break;
}
return [];
}
console.log(binarySearch([1, 2, 3, 4, 5], 3));
console.log(binarySearch([1, 2, 3, 4, 5, 9], 4));
```

View File

@@ -0,0 +1,55 @@
---
id: 6996cfb6c01e9a802add38be
title: Step 13
challengeType: 1
dashedName: step-13
---
# --description--
Finally, if the `value` is not found at the middle and is also not greater than `valueAtMiddle`, then the `value` must be less than `valueAtMiddle`, and must be on the left. The final `else` block will handle this.
So, finish up the loop by removing the `break` keyword and adding an `else` block. Inside it, update the `high` variable by subtracting `1` from `mid`.
# --hints--
You should replace the `break` keyword with an `else` block that updates the `high` variable to `mid - 1`.
```js
assert.match(
__helpers.removeJSComments(String(binarySearch)),
/else\s*\{\s*high\s*=\s*mid\s*-\s*1\s*;?\s*\}/
);
```
# --seed--
## --seed-contents--
```js
function binarySearch(searchList, value) {
let pathToTarget = [];
let low = 0;
let high = searchList.length - 1;
while (low <= high) {
let mid = Math.floor((low + high) / 2);
let valueAtMiddle = searchList[mid];
pathToTarget.push(valueAtMiddle);
if (value === valueAtMiddle) {
return pathToTarget;
} else if (value > valueAtMiddle) {
low = mid + 1;
}
--fcc-editable-region--
break;
--fcc-editable-region--
}
return [];
}
console.log(binarySearch([1, 2, 3, 4, 5], 3));
console.log(binarySearch([1, 2, 3, 4, 5, 9], 4));
```

View File

@@ -0,0 +1,57 @@
---
id: 6996d0a2c2ac55041145a13f
title: Step 14
challengeType: 1
dashedName: step-14
---
# --description--
Did you notice the second function call now has the values `3, 5, 4` in the list? That indicates the binary search is working as intended.
This is how it happened: The algorithm first checked `3` as the middle of the initial list. Since `4` is greater than `3`, the search shifted to the right half. It then examined `5` as the new middle. Because `4` is less than `5`, the search moved to the left, ultimately identifying `4` as the middle of the final range.
To test the function again, call it with `[1, 3, 5, 9, 14, 22], 10` and log it to the console. This is a situation in which the value will not be found.
# --hints--
You should call the function with `binarySearch([1, 3, 5, 9, 14, 22], 10)` and log it to the console.
```js
assert.match(
__helpers.removeJSComments(code),
/console\s*\.\s*log\s*\(\s*binarySearch\s*\(\s*\[\s*1\s*,\s*3\s*,\s*5\s*,\s*9\s*,\s*14\s*,\s*22\s*\]\s*,\s*10\s*\)\s*\)/
);
```
# --seed--
## --seed-contents--
```js
function binarySearch(searchList, value) {
let pathToTarget = [];
let low = 0;
let high = searchList.length - 1;
while (low <= high) {
let mid = Math.floor((low + high) / 2);
let valueAtMiddle = searchList[mid];
pathToTarget.push(valueAtMiddle);
if (value === valueAtMiddle) {
return pathToTarget;
} else if (value > valueAtMiddle) {
low = mid + 1;
} else {
high = mid - 1;
}
}
return [];
}
console.log(binarySearch([1, 2, 3, 4, 5], 3));
console.log(binarySearch([1, 2, 3, 4, 5, 9], 4));
--fcc-editable-region--
--fcc-editable-region--
```

View File

@@ -0,0 +1,53 @@
---
id: 6996d1032feed88e2957dc8e
title: Step 15
challengeType: 1
dashedName: step-15
---
# --description--
You can see that the last call prints an empty array. To further specify that the value is not in the searched array, update the `return` statement to return an array containing two elements, an empty array, and the string `Value not found`.
# --hints--
You should update the `return` statement to return `[], 'Value not found'`.
```js
assert.match(
__helpers.removeJSComments(String(binarySearch)),
/return\s*\[\s*\[\s*\]\s*,\s*('|"|`)Value not found\1\s*\]/
);
```
# --seed--
## --seed-contents--
```js
function binarySearch(searchList, value) {
let pathToTarget = [];
let low = 0;
let high = searchList.length - 1;
while (low <= high) {
let mid = Math.floor((low + high) / 2);
let valueAtMiddle = searchList[mid];
pathToTarget.push(valueAtMiddle);
if (value === valueAtMiddle) {
return pathToTarget;
} else if (value > valueAtMiddle) {
low = mid + 1;
} else {
high = mid - 1;
}
}
--fcc-editable-region--
return [];
--fcc-editable-region--
}
console.log(binarySearch([1, 2, 3, 4, 5], 3));
console.log(binarySearch([1, 2, 3, 4, 5, 9], 4));
console.log(binarySearch([1, 3, 5, 9, 14, 22], 10));
```

View File

@@ -0,0 +1,89 @@
---
id: 6996d18b31aab5cbbffbc731
title: Step 16
challengeType: 1
dashedName: step-16
---
# --description--
One final small enhancement you can add is to also show the index at which the target `value` is found.
So, instead of returning only `pathToTarget` in the `if` statement, add a template literal with the message `Value found at index ${mid}`.
With that, your binary search algorithm workshop is complete!
# --hints--
You should return `pathToTarget, \`Value found at index ${mid}\`` inside the `if` statement.
```js
assert.match(
__helpers.removeJSComments(String(binarySearch)),
/return\s*\[\s*pathToTarget\s*,\s*(?:`Value found at index\s*\$\{\s*mid\s*\}`|['"]Value found at index\s*['"]\s*\.\s*concat\(\s*mid\s*\))\s*\]/
);
```
# --seed--
## --seed-contents--
```js
function binarySearch(searchList, value) {
let pathToTarget = [];
let low = 0;
let high = searchList.length - 1;
while (low <= high) {
let mid = Math.floor((low + high) / 2);
let valueAtMiddle = searchList[mid];
pathToTarget.push(valueAtMiddle);
if (value === valueAtMiddle) {
--fcc-editable-region--
return pathToTarget;
--fcc-editable-region--
} else if (value > valueAtMiddle) {
low = mid + 1;
} else {
high = mid - 1;
}
}
return [[], "Value not found"];
}
console.log(binarySearch([1, 2, 3, 4, 5], 3));
console.log(binarySearch([1, 2, 3, 4, 5, 9], 4));
console.log(binarySearch([1, 3, 5, 9, 14, 22], 10));
```
# --solutions--
```js
function binarySearch(searchList, value) {
let pathToTarget = [];
let low = 0;
let high = searchList.length - 1;
while (low <= high) {
let mid = Math.floor((low + high) / 2);
let valueAtMiddle = searchList[mid];
pathToTarget.push(valueAtMiddle);
if (value === valueAtMiddle) {
return [pathToTarget, `Value found at index ${mid}`];
} else if (value > valueAtMiddle) {
low = mid + 1;
} else {
high = mid - 1;
}
}
return [[], "Value not found"];
}
console.log(binarySearch([1, 2, 3, 4, 5], 3));
console.log(binarySearch([1, 2, 3, 4, 5, 9], 4));
console.log(binarySearch([1, 3, 5, 9, 14, 22], 10));
```

View File

@@ -0,0 +1,75 @@
{
"isUpcomingChange": true,
"dashedName": "workshop-binary-search-js",
"helpCategory": "JavaScript",
"blockLayout": "challenge-grid",
"challengeOrder": [
{
"id": "6995d0325fd7da40854fac40",
"title": "Step 1"
},
{
"id": "6996c60281feca6718cc15c1",
"title": "Step 2"
},
{
"id": "6996c675b599baa7b103192d",
"title": "Step 3"
},
{
"id": "6996c7363a4a437839f1f15d",
"title": "Step 4"
},
{
"id": "6996c796385b6ea4e222d5e8",
"title": "Step 5"
},
{
"id": "6996c8375f4301004459ee85",
"title": "Step 6"
},
{
"id": "6996c8f94fe6b250f11a2a1e",
"title": "Step 7"
},
{
"id": "6996c95e5bede03bdf2937a2",
"title": "Step 8"
},
{
"id": "6996ca21a74104721f2c7e5d",
"title": "Step 9"
},
{
"id": "6996cb5869acf58de751ae81",
"title": "Step 10"
},
{
"id": "6996ccadf9048c79b857bb15",
"title": "Step 11"
},
{
"id": "6996cdf1f60cbc30eeeae1c9",
"title": "Step 12"
},
{
"id": "6996cfb6c01e9a802add38be",
"title": "Step 13"
},
{
"id": "6996d0a2c2ac55041145a13f",
"title": "Step 14"
},
{
"id": "6996d1032feed88e2957dc8e",
"title": "Step 15"
},
{
"id": "6996d18b31aab5cbbffbc731",
"title": "Step 16"
}
],
"blockLabel": "workshop",
"usesMultifileEditor": true,
"hasEditableBoundaries": true
}

View File

@@ -315,6 +315,7 @@
"comingSoon": true,
"blocks": [
"lecture-introduction-to-common-searching-and-sorting-algorithms",
"workshop-binary-search-js",
"lab-bubble-sort-algorithm",
"lab-selection-sort-js",
"lab-insertion-sort",