The Testing Academy · Class Notes Wednesday, 21 October (IST)
Live class · study guide

JavaScript arrays: why sort() puts 10 before 2, compare functions and localeCompare, slice and negative indexes, concat and spread, Array.isArray, every and some, copy versus reference, and destructuring

Why sort() turns [10, 1, 21, 2] into [1, 10, 2, 21], and the compare functions that fix it. Then slice with negative indexes, concat and spread, Array.isArray, every and some, copy versus reference and destructuring, up to lab 100. Files 90 to 100 and two notes files written with AI, pushed to the batch repo as the class ended.

By Pramod Dutta, The Testing Academy. Study notes from the eleventh class of Playwright 4x, built from the session recording and the batch repository, which received files 90 to 100, two notes files and two cheat-sheet images as the class ended (commit "feat: add array lessons 90-100 and README sections"). Every file was run with Node 22, and the output shown is what it prints. Where a file keeps its examples commented out, it is shown as pushed, then with the examples switched on, the way the class ran them.

01

What this class covered

  • A recap of arrays so far
  • sort(): alphabetical by default, and it changes the original array
  • Why [10, 1, 21, 2] sorts to [1, 10, 2, 21]: lexicographic, or dictionary, order
  • Asking AI to explain a method, and keeping the answer as a notes file
  • Compare functions: a - b, b - a, and localeCompare for text
  • slice: start, end, negative indexes, and why slice(-3, -5) is empty
  • Combining arrays: concat and spread
  • Array.isArray
  • every and some
  • Copy versus reference
  • Destructuring: by position, rest and skipping
  • Lab 100: swapping two variables, and what comes next
  • Tasks: the practice list, five array tasks, and tonight's Agent Skills for QA session
02

A recap of arrays so far

The last class covered length and indexes, at(), typeof an array (an object), mixed types, four ways to create an array, changing items by index, push, pop, shift, unshift and splice, indexOf, lastIndexOf, includes and find, four ways to loop, and map and filter: filter keeps only the items that pass a test, and map keeps the length but transforms every item.

Today: the rest of the array chapter. Sorting, slicing, combining and copying arrays, and destructuring them.

03

sort(): alphabetical by default

Lab 90:

JavaScript
let fruits = ["banana", "apple", "cherry"];
console.log(fruits);
fruits.sort();
console.log(fruits);
//  alphabetical by default 
Text
[ 'banana', 'apple', 'cherry' ]
[ 'apple', 'banana', 'cherry' ]

sort() is built in. Hover over it in VS Code and the editor shows its documentation: it sorts the array in place, changing the array itself, and returns a reference to that same array. So the same fruits prints twice: banana, apple, cherry before, and apple, banana, cherry after, ordered by their first characters.

One point from the AI explanation the class read: the brackets matter. fruits.sort without them only refers to the method and never runs it.

JavaScript
let fruits = ["banana", "apple", "cherry"];
fruits.sort;
console.log(fruits);
Text
[ 'banana', 'apple', 'cherry' ]
04

Why numbers sort as text

Lab 91:

JavaScript
// let score = [4,3,2];
// console.log(score.sort());

let nums = [10,1,21,2];
nums.sort();
console.log(nums);
// Natural Sorting, Lexicographic Sort


let n = [11,-11];
console.log(n.sort())
Text
[ 1, 10, 2, 21 ]
[ -11, 11 ]

The commented example at the top, [4, 3, 2], gives [ 2, 3, 4 ] when switched on: single digits come out right. Two digits do not. The class expected 1, 2, 10, 21 and got 1, 10, 2, 21.

With no compare function, sort() turns every item into a string and compares the strings one character at a time, from the left. That is lexicographic order, the order of a dictionary:

  • "10" against "2": the first characters are "1" and "2", and "1" is smaller, so "10" comes first. The 0 is never looked at.
  • "1" against "10": they match on the "1", then "1" runs out of characters. The shorter one comes first, the way car comes before cart in a dictionary.
  • When the first characters match, the second decides: 10 comes before 11, and 21 before 23.
Two panels. Left, titled Dictionary order, with the sublabel car before cart: an open dictionary with two entries marked, the shorter one first. Right, titled Default sort(), with the sublabel 10 before 2: a tray of four tiles filed narrow, wide, narrow, wide, with the first wide tile outlined in terracotta.
With no compare function, sort() files numbers the way a dictionary files words, one character at a time: 1, 10, 2, 21.

Lexicographic, not natural. The class also called this order "natural sorting", and so does the comment in the file. The repo's README points out that natural sorting usually means the number-aware order people expect, file2 before file10. What sort() does by default is plain lexicographic order.

[11, -11] comes out as [ -11, 11 ]. That looks properly sorted, but it is still text order: "-" has the character code 45 and "1" has 49, as the README notes. Two negative numbers show it:

JavaScript
console.log([-1, -2].sort());
Text
[ -1, -2 ]

-2 is the smaller number, but as text "-1" comes first.

Character codes. The class asked AI to explain lexicographic order and kept the answer as 91_Lexicographic_Explain.md, beside the code. Its table of codes:

Characters Codes
"0" to "9" 48 to 57
"A" to "Z" 65 to 90
"a" to "z" 97 to 122

So digits come before uppercase letters, and uppercase letters come before lowercase ones. The notes file's two examples, run with console.log:

JavaScript
console.log(["banana", "Apple", "cherry"].sort());
console.log(["b", "B", "a", "A"].sort());
Text
[ 'Apple', 'banana', 'cherry' ]
[ 'A', 'B', 'a', 'b' ]
A cheat sheet titled sort(): Text vs Numbers. On the left, [10, 1, 21, 2].sort() with a red arrow down to four red-outlined cells, 1, 10, 2 and 21, labelled compared as text. On the right, .sort((a, b) => a - b) with a green arrow down to four green-outlined cells, 1, 2, 10 and 21, labelled compared as numbers. A banner at the bottom reads: sort() changes the original array.
From the batch repo's README: the same four numbers, compared as text and as numbers.
05

Asking AI to explain a method, and keeping the answer

How the instructor works out a method they do not know, step by step:

  1. Hover over the method in VS Code to read its built-in documentation.
  2. Select the code and ask GitHub Copilot, for example "explain sort in short".
  3. Ask follow-up questions until the idea is clear. Here: what does lexicographic mean, and why does slice(-3, -5) print an empty array?
  4. Keep the answer as a markdown file beside the code, numbered with its lab: 91_Lexicographic_Explain.md and 93_Slice_Explain.md. Right-click the file and choose Open Preview to read it formatted.
  5. Code inside markdown goes between two lines of three backticks, with the language name after the first three: javascript.

"I am teaching you how to fish," as the instructor put it: learn the concept in class, then use AI to research it, and keep what you learn as notes. Asked whether the notes belong in a separate folder, the class chose the same folder as the code, where they are easier to remember.

Two panels. Left, titled Given a fish, with the sublabel One answer: a cooked fish on a plate beside a fork. Right, titled Taught to fish, with the sublabel Ask, then keep notes: a fishing rod leaning on an open tackle box that holds a coil of line and a notebook.
The instructor's advice: learn the concept in class, use AI to research the rest, and keep each answer as a notes file beside the code.
06

Compare functions: a - b, b - a and localeCompare

Lab 92:

JavaScript
let nums = [10,1,21,2];
nums.sort((a,b) => a-b);
console.log(nums);
nums.sort((a,b) => b-a);
console.log(nums);

let fruits = ['apple','banana',"cherry"];
fruits.sort((a, b) => a.localeCompare(b))


// arrow Functions
Text
[ 1, 2, 10, 21 ]
[ 21, 10, 2, 1 ]

Pass sort() a function and it stops comparing text. The function receives two items, a and b, and returns a number:

  • negative: a goes first
  • zero: keep their order
  • positive: b goes first

a - b is negative whenever a is the smaller number, so the smaller number goes first: ascending. b - a flips every sign, so the largest comes first: descending. A question from class: decimals work the same way.

JavaScript
console.log([1.5, 0.25, 10.75, 2].sort((a, b) => a - b));
Text
[ 0.25, 1.5, 2, 10.75 ]

On the iPad, the class walked through it as pairs being compared and swapped, again and again, until every pair is in order. The detail that matters: sort() decides which two items to compare, and in what order; the function only answers "which of these two goes first?". Add a console.log inside it to watch the questions Node asks:

JavaScript
let nums = [10, 1, 21, 2];
nums.sort((a, b) => {
  console.log(a, b, a - b);
  return a - b;
});
console.log(nums);
Text
1 10 -9
21 1 20
21 10 11
2 10 -8
2 1 1
[ 1, 2, 10, 21 ]
Question a b a - b Answer
1 1 10 -9 negative: 1 goes before 10
2 21 1 20 positive: 1 goes before 21
3 21 10 11 positive: 10 goes before 21
4 2 10 -8 negative: 2 goes before 10
5 2 1 1 positive: 1 goes before 2

Five answers, and the order is settled: 1, 2, 10, 21. Which pairs get compared is up to the JavaScript engine; the rule for each answer is the same everywhere.

THE SAME FOUR NUMBERS, SORTED TWO WAYS No compare function nums.sort() THE ARRAY [10, 1, 21, 2] four numbers TURNED INTO TEXT "10" "1" "21" "2" compared character by character RESULT [1, 10, 2, 21] text order With a compare function nums.sort((a, b) => a - b) A PAIR a and b two items sort() picks THE FUNCTION a - b one number back NEGATIVE a goes first ZERO keep their order POSITIVE b goes first 1 - 10 is -9: 1 goes before 10 21 - 1 is 20: 1 goes before 21 [1, 2, 10, 21] number order b - a flips every answer, so the largest comes first: [21, 10, 2, 1]
Files 91 and 92 side by side. Without a function, sort() compares text. With one, it only asks "which of these two goes first?", and the sign of the answer decides.

(a, b) => a - b is an arrow function, a short way to write a function. Functions are the next chapter, and the class will come back to this line then.

Text: localeCompare. For strings, the class used a.localeCompare(b), a built-in that compares two strings the way people expect. The fruits line in file 92 sorts but never prints. With console.log(fruits); added after it, the file prints:

Text
[ 1, 2, 10, 21 ]
[ 21, 10, 2, 1 ]
[ 'apple', 'banana', 'cherry' ]

The fruits were already in order, as the class noted, so nothing moves. The notes file describes localeCompare as sorting text "ignoring case the way people expect". Strictly, it puts each letter's two cases together and lets the case break the tie. The same four letters as before:

JavaScript
console.log(["b", "B", "a", "A"].sort((a, b) => a.localeCompare(b)));
Text
[ 'a', 'A', 'b', 'B' ]

Compare [ 'A', 'B', 'a', 'b' ] from the default sort.

07

slice: copy part of an array

Lab 93:

JavaScript
// Slice & Combining

let arr = [1, 2, 3, 4, 5];
// slice(start, end) - returns new array, 
// does NOT mutate actual -> ( start, end-1) = index = 0
// Don't give the end, it will automatically 
// take from start to end.

// console.log(arr.slice(1, 3));
console.log(arr);

// console.log(arr.slice(2));

// console.log(arr.slice(-2));
// console.log(arr.slice(-3));

// console.log(arr.slice(0));

// console.log(arr.slice(-5));

console.log(arr.slice(-3,-5));
Text
[ 1, 2, 3, 4, 5 ]
[]

Only two lines run: the array itself, and arr.slice(-3, -5), which prints an empty array. The class switched the other examples on one at a time. Each one, run on its own:

Call With positive indexes Result What it takes
arr.slice(1, 3) slice(1, 3) [ 2, 3 ] index 1 up to, not including, 3
arr.slice(2) slice(2, 5) [ 3, 4, 5 ] no end: from index 2 to the last item
arr.slice(-2) slice(3, 5) [ 4, 5 ] the last two
arr.slice(-3) slice(2, 5) [ 3, 4, 5 ] the last three
arr.slice(0) slice(0, 5) [ 1, 2, 3, 4, 5 ] the whole array, as a copy
arr.slice(-5) slice(0, 5) [ 1, 2, 3, 4, 5 ] the whole array, as a copy
arr.slice(-3, -5) slice(2, 0) [] start after end: nothing to copy

The rules, as the class put them:

  • slice(start, end) returns a new array. It never changes the original.
  • It takes the items from start up to end minus 1: the end itself is not included, because slice works on indexes.
  • Leave the end out, and it takes everything from the start to the last item.
  • Negative indexes count from the end: -1 is the last item, and on this five-item array -5 is the first.
  • slice always moves forward, from left to right.

Why is slice(-3, -5) empty? The class asked AI and kept the answer as 93_Slice_Explain.md. Its rule of thumb: turn each negative number into length plus that number. -3 becomes 5 + (-3) = 2, and -5 becomes 5 + (-5) = 0, so the call is slice(2, 0): a start after the end. slice never goes backwards and never throws an error; it returns []. To get [1, 2], put the smaller number first, as the notes file shows:

JavaScript
let arr = [1, 2, 3, 4, 5];
console.log(arr.slice(-5, -3));
console.log(arr.slice(-3, -1));
Text
[ 1, 2 ]
[ 3, 4 ]
ONE ARRAY, TWO WAYS TO COUNT, AND WHAT SLICE COPIES 0 1 -5 1 2 -4 2 3 -3 3 4 -2 4 5 -1 index counts from 0 negative index length + n, so -2 is 3 let arr = [1, 2, 3, 4, 5]; arr.slice(1, 3) 1 2 3 4 5 [2, 3] end not included arr.slice(-2) 1 2 3 4 5 [4, 5] no end: to the last item arr.slice(-3, -5) 1 2 3 4 5 [] slice(2, 0): start after end start: index 2 end: index 0 slice copies; arr itself stays [1, 2, 3, 4, 5]. splice, from the last class, changes it.
File 93 in one picture. Turn any negative number into length + n first; if the start is not before the end, the copy is empty.

slice and splice look alike and are completely different. splice, from the last class, removes and inserts items in the original array. slice copies, and leaves the original alone.

08

Combining arrays: concat and spread

Lab 94:

JavaScript
let a = [1, 2];
let b = [3, 4];
let c = a.concat(b);
console.log(c);

// spread (modern way) - concatenation. (...)
let d = [...a,...b];
console.log(d);
Text
[ 1, 2, 3, 4 ]
[ 1, 2, 3, 4 ]

a.concat(b) joins two arrays, the way strings are concatenated. The modern way is the spread operator, three dots: ...a means all the items of a, so [...a, ...b] builds one new array from all of a followed by all of b. Both give the same result, and neither changes a or b.

09

Array.isArray

Lab 95:

JavaScript
let result = Array.isArray([1, 2, 3]);
let result1 = Array.isArray("a");
console.log(result);
console.log(result1);
Text
true
false

Array.isArray() answers one question: is this an array? true for [1, 2, 3], and false for "a", which is a plain string. It is the check to use because typeof an array only says "object", as the last class found.

10

every and some

map and filter, from the last class, transform an array. every and some answer one question about the whole array, with a single boolean.

Lab 96:

JavaScript
// Map and Filters


// every vs some

// every - ALL must pass
// console.log([80, 90, 85].every(i => i >= 70));
// console.log([80, 60, 85].every(i => i >= 70));


// some - AT LEAST ONE must pass
console.log([80, 60, 85].some(s => s < 70));
console.log([80, 90, 85].some(s => s < 70)); // false
Text
true
false

The two every lines are commented out. Switch them on and the file prints all four answers:

Text
true
false
true
false
Call Each item checked Result
[80, 90, 85].every(i => i >= 70) 80 true, 90 true, 85 true: all passed true
[80, 60, 85].every(i => i >= 70) 80 true, 60 false: stops here false
[80, 60, 85].some(s => s < 70) 80 false, 60 true: stops here true
[80, 90, 85].some(s => s < 70) 80 false, 90 false, 85 false: none passed false
  • every: all must pass. One failure makes the answer false.
  • some: at least one must pass. One success makes the answer true.

A comparison from the chat, which the class agreed with: every is like an AND, and some is like an OR. The repo's README adds that both stop as soon as the answer is known, which the table shows: every stops at the first false, and some at the first true. i and s are only names for the current item; any name works.

11

Copy versus reference

Lab 97:

JavaScript
let original = [1, 2, 3];
let copy1 = [...original];
// console.log(original);
// console.log(copy1);

let copy2 = original.slice();
copy2.push(92);
copy1.push(93);
console.log(original);
console.log(copy1);
console.log(copy2);



// Deep cOPY
let deep_copy_array = original;
// Deep copy

deep_copy_array.push(91);
console.log(original);
console.log(deep_copy_array);
Text
[ 1, 2, 3 ]
[ 1, 2, 3, 93 ]
[ 1, 2, 3, 92 ]
[ 1, 2, 3, 91 ]
[ 1, 2, 3, 91 ]
  • copy1 = [...original] and copy2 = original.slice() are new arrays. Pushing 93 into one and 92 into the other leaves original as [ 1, 2, 3 ].
  • deep_copy_array = original copies nothing. Both names now point at one array, so pushing 91 through deep_copy_array changes original too: the last two lines print the same [ 1, 2, 3, 91 ].

Arrays are mutable, and this is one of the places JavaScript surprises people. To copy an array, use slice or spread. The instructor uses spread throughout.

Not a deep copy. The file labels deep_copy_array = original a deep copy, and the class used the same words. It is no copy at all: one array with two names, which is why original gets the 91 too. The repo's README makes the same correction. In interview terms, spread and slice() make a shallow copy, a new array whose nested arrays are still shared, and a deep copy duplicates nested arrays too. The README's tool for that is structuredClone().

A cheat sheet titled Copy vs Reference. The left panel, Reference, shows let ref = original; with two labels, original and ref, both pointing at one array of 1, 2, 3, and the line: one array: change one, both change. The right panel, Copy, shows let copy = [...original]; with original and copy each pointing at its own array of 1, 2, 3, and the line: two arrays: changes stay separate. A banner at the bottom reads: Nested arrays need structuredClone().
From the batch repo's README: = shares one array; spread makes a second one.
12

Destructuring

Lab 98:

JavaScript
let [first, second, third] = [10, 20, 30];

console.log(first);
console.log(second);
console.log(third);
Text
10
20
30

Destructuring unpacks an array into variables by position: 10 goes to first, 20 to second, 30 to third.

Lab 99:

JavaScript
let [a, b, ...rest] = [10, 20, 30, 40, 50];
console.log(a);    // 10
console.log(b);    // 20
console.log(rest); // [30, 40, 50]


let [, , thirdOnly] = [10, 20, 30];
console.log(thirdOnly);
Text
10
20
[ 30, 40, 50 ]
30

...rest collects every item left over: 30, 40 and 50, as a new array. An empty slot skips a position, so [, , thirdOnly] skips 10 and 20 and takes the 30. The class called it a very unique concept.

Rest, not spread. The class called ...rest spread. It is the same three dots doing the opposite job. In [...a, ...b] they spread an array out into separate items; on the left of = they collect the remaining items into a new array, and that is called the rest element. The repo's README uses that name.

13

Lab 100: swapping two variables

JavaScript
// Swapping
let p = 1, q = 2;
[p, q] = [q, p];
console.log(p, q);
Text
2 1

The right side is worked out first: [q, p] is the array [2, 1]. Destructuring then fills the left side by position, so p becomes 2 and q becomes 1. That was lab 100: a hundred exercises so far, and congratulations from the instructor.

DESTRUCTURING: NAMES ON THE LEFT, FILLED BY POSITION let [a, b, ...rest] = [10, 20, 30, 40, 50]; 10 20 30 40 50 a b rest a = 10, b = 20, rest = [30, 40, 50] let [, , thirdOnly] = [10, 20, 30]; 10 20 30 thirdOnly skipped skipped thirdOnly = 30 Swapping two variables let p = 1, q = 2; [p, q] = [q, p]; BEFORE p = 1, q = 2 two variables RIGHT SIDE FIRST [q, p] is [2, 1] a new array of the values THEN THE LEFT p = 2, q = 1 filled by position
Files 98 to 100. The right side is worked out first; then each name on the left takes the item in its position. An empty slot skips an item, and ...rest takes everything left over.

What comes next. Multi-dimensional arrays, 2D and 3D, are still to come; they need for loops. First the chapter on functions, which explains the arrow functions used today, then closures, then multi-dimensional arrays.

14

Tasks and announcements

  • The practice list. Everyone should finish the practice programs up to the triangle classifier: Hello World, taking user input, printing, FizzBuzz and the grade calculator are on it too. Meeti is sharing the list as a screenshot.
  • Five array tasks, to try on your own before the next class, which works through them: the largest item, the smallest item, the sum and the reverse of an array. The list on screen also had frequency and duplicates. Meeti is sharing the exact five. A hint from class: filter helps.
  • Doubts go in today's doubt thread, which Meeti posts.
  • Tonight, 8 PM IST: Agent Skills for QA. What a skill is, how one is structured, how to build one from scratch with AI, and more than 36 ready-to-use skills, with notes. The Playwright 4x batch is invited. The usual club-class link goes live around 6 PM. There is a recording, but come live if you can.
  • More AI sessions will be added alongside the course. The Playwright course's own AI part covers Playwright MCP, Playwright AI agents and the Playwright CLI. For AI in depth, a separate weekend batch, AI Architect QA, starts on Sunday, 25 October; the team has the details.
  • Attendance: on app.thetestingacademy.com, open My Progress and use "Add today's learning".
  • The push. Files 90 to 100, the two notes files and two cheat-sheet images went up to the batch repo as the class ended.

Task, five array tasks. Write programs that find the largest and the smallest item in an array, add up its items, reverse it, and count how often each item appears or find the ones that repeat.

The question the class flagged as important. After let b = a; b.push(1);, has a changed? And after let c = [...a]; c.push(2);? Explain why, using lab 97.