初めに
勉強ノート4まで知らないといけない基礎概念を自分なりにまとめてきました。やっとですね。これからは問題練習の振り返りや、視覚化したソートをJSで書いてみたアルゴリズムのまとめです。
では、今回も勉強でとったメモを振り返りとしてまとめていきたいと思います。
typeof
console.log('typeof true', typeof true) // boolean
console.log('typeof 30', typeof 30) // number
console.log('typeof 0.5', typeof 0.5) // number
console.log('typeof "hello"', typeof 'hello') // string
console.log('typeof "30"', typeof '30') // string
console.log('typeof BigInt', typeof BigInt) // function
// console.log('typeof BigInt', typeof 'BigInt') // string
console.log('typeof BigInt', typeof 1n) // bigint
console.log('typeof BigInt', typeof BigInt('1')) // bigint
console.log('typeof BigInt', typeof Object(1n)) // bigint
console.log('typeof Symbol', typeof Symbol) // function
// console.log('typeof Symbol', typeof 'Symbol') // string
let sym = Symbol('abc')
console.log('typeof Symbol', typeof Symbol('foo')) // symbol
console.log('typeof Symbol', typeof sym) // symbol
console.log('typeof Symbol', typeof Object(sym)) // object
console.log('typeof [1]', typeof [1]) // object
console.log('typeof {a: 1}', typeof { a: 1 }) // object
console.log('typeof null', typeof null) // object
console.log('typeof undefined', typeof undefined) // undefined
console.log('typeof NaN', typeof NaN) // number
console.log('typeof function', typeof function () { }) // function
let fn = function () {
return
}
console.log('typeof function', typeof fn) // function
// Primitive values: Boolean, Number, String, "BigInt", "Symbol", "undefined", "null"
// Objects: Array, Object, function
typeof:タイプ検査のメソッドです。よくif条件式で厳密等価/厳密不等価と一緒に使われますが、自分のやり方としては一回タイプをコンソール出力してみてから、条件に入れます。
面白い結果としては、BigIntとSymbolはfunctionでした。でも確かに引数を入れて巨大な数字を返したり、一意の値を返したりするんですね。メソッドファンクションみたいけれど、自分の型が持っている感じです。
自分にとって要注意なのは、
- Object()に包まれたもの、全部Objectになる。
- nullはObject。
nullは存在しない値ですが、Objectとして判断されているので、予想外のことが起こるかもしれません。↓
function check(x) {
if (typeof x === 'object') {
console.log('Object')
}
}
check([1]) // Object
check({ a: 1 }) // Object
check(null) // Object
xが真値に当たるObjectの同時に、偽値のnullが入れられるのも可能になります。なので、書き方を修正するしかありません。
function check(x) {
if (!x) return
if (typeof x === 'object') {
console.log('OK')
}
}
check([1]) // OK
check({ a: 1 }) // OK
check(null)
動作の前に、偽値(falsy value)をedge case(エッジケース)だと想定して排除しましょう。ここで排除したのはすべての偽値でした。
if
let number = 10
if (number % 5 === 0) { // 0 === 0 => true
console.log('it is multiple of 5')
} else {
console.log('it is not')
}
// it is multiple of 5
//
if ((number % 5) === 0) { // 0 === 0 => true
console.log('it is multiple of 5')
} else {
console.log('it is not')
}
// it is multiple of 5
if (!(number % 5)) { // !0 => true, 0 is falsy value
console.log('it is multiple of 5')
} else {
console.log('it is not')
}
// it is multiple of 5
if...else:いろんな条件の判断を通して、やることを導くif条件式です。
偽値やを理論否定(logical NOT)使って条件式を操作するととても便利ですが、最初使うときよく自分の想像と違う結果が出て、慣れるまでケースを想定してコンソール出力で検証したほうがいいのではないかと。自分もまだ慣れてはないんです。
偽値とその使用がどんな結果になるのか、こちらの参考資料へ
Falsy (偽値) - MDN Web Docs 用語集: ウェブ関連用語の定義 | MDN
JS Comparison Table
/*
age >= 65, old man
65 > age >= 20, young
age < 20, children
*/
// case
let age = Math.floor((Math.random() * 100)) + 1
console.log(age)
// age = 0 // or '' or false -> console.log('children')
if (age >= 65) {
console.log('old man')
} else if (age >= 20) {
console.log('young')
} else {
console.log('children')
}
age = 0 // or '' or false // 0, '0', '' is falsy value
// solution 1
if (age >= 65) {
console.log('old man')
} else if (age >= 20) {
console.log('young')
} else if (age > 0) {
console.log('children')
} else {
console.log('it does not exist...')
}
// solution 2
if (age >= 65) {
console.log('old man')
} else if (65 > age && age >= 20) {
console.log('young')
} else if (age < 20 && age > 0) {
console.log('children')
} else {
console.log('it does not exist...')
}
if 実践編
// random 1~100
let score = Math.floor((Math.random() * 100)) + 1
console.log(score)
// solution
if (score === 100) {
console.log('you are no1')
} else if (score >= 60) {
console.log('pass')
} else {
console.log('fail')
}
// BMI = weight / height(m)^2
// solution 1
let weight = 70
let height = 180 / 100
let bmi = weight / height ** 2
console.log(Math.round(bmi)) // 22
// test
// bmi = 24
// bmi = 27
// bmi = 30
// bmi = 35
// solution 2
// function getRandom(min, max) {
// return Math.floor(Math.random() * (max - min + 1)) + min
// }
// let weight = getRandom(0, 100)
// let height = getRandom(100, 180) / 100
// const bmi = Math.round(weight / height ** 2)
// console.log(bmi)
if (bmi < 18.5) {
console.log('underweight')
} else if (bmi < 24) {
console.log('normal')
} else if (bmi < 27) {
console.log('overweight')
} else if (bmi < 30) {
console.log('Mild obesity')
} else if (bmi < 35) {
console.log('Moderate obesity')
} else {
console.log('Severe obesity')
}
ちょっと遊び心でランダム数字作成のfunctionを書いてみました。
Loop - goto label 概念
// concept
var i = 1
label:
console.log(i)
i++
if (i <= 100) {
goto label
}
goto label 概念:goto文はJSの中に存在しないけれど概念的にwhileとfor loopに近いものです。pseudo codeを書くときgoto文がよく目にしたので、気になって調べたらC言語で使われるLoopの基本概念だったと。JSと書き方が違ってても仕組みはほぼ一緒だと感じています。
やらせたいことをlabelのところにする。つまりLoopの本体ですね。
if条件に合う時Loopが成立し、そしてどんな段階でgotoを実行、labelに行ってやることを終わったらまた条件式に戻って判断を行います。
do while
// do while
var i = 1
do {
console.log(i) // 9 // 10
i++
console.log(i) // 10 // 11
if (i % 2 === 1) { // if it is odd number
continue // get back to while (i <= 10)
}
console.log('i++', i) // 10
} while (i <= 10) // 10 <= 10 is true // 11 <= 10 is false
console.log('i=', i) // 11
do while:最初はdo whileがwhileやforと比べて、あまりにも書き方が違うと戸惑ってあまり使いませんでしたが、goto文が分かった後、ああ、これはまさにlabelとgoto文の書き方だなあって思いました。
forと違って、i++が好きなところにおくことができます。(でもいまじゃdo whileあまり使われてない気がする)
while
var i = 1
while (i <= 10) {
console.log(i++) // print i ⇒ i++
}
console.log('i=', i) // 11
while:do whileと似ている、もっと簡潔化した書き方だと思います。
最初この書き方を目にしたときちょっとびっくりした。iの出力も、increment運算子も一緒に動作しました。
do whileよりまだよく使われて、回数が分からないときwhileを使うようにしています。
for loop
// var i = 1 // start condition
// while (i <= 100) { // end condition
// console.log(i)
// //...
// i++ // increment
// }
// for (start; end; final-expression) {}
for (var i = 1; i <= 10; i++) {
console.log(i)
}
for (var i = 1; i <= 10; i *= 2) {
if (i % 2) continue // even number % 2 = 0 (falsy value) => false
console.log(i)
}
// 2
// 4
// 8
var scores = [10, 24, 35, 48, 50]
var sum = 0
var i = 0
for (i = 0; i <= scores.length - 1; i++) {
sum += scores[i] // sum = sum + scores[i]
}
console.log(sum) // 167
// while (i < scores.length) {
// sum += scores[i]
// i++
// }
// console.log(sum) // 167
for loop:よく使っているLoopです。定型のフォーマットで書いてたら、whileやdo whileのように算術演算子のことを忘れて無限ループにしてしまうことが比較的に少ないし、まわる回数が分かってれば基本的にforを使っています。
よく使うArray methods
// join() // String
let arr = [1, 2, 3]
console.log(arr.join('!')) // 1!2!3 // String
console.log(arr.join('')) // 123
// map() // iterate object with function
function double(x) {
return x * 2
}
console.log(arr.map(double)) // [ 2, 4, 6 ]
function negative(x) {
return x * -1
}
console.log(arr.map(negative)) // [ -1, -2, -3 ]
// anonymous
console.log(arr
.map(x => x * -1)
.map(x => x * 2)
) // [ -2, -4, -6 ]
// filter() // iterate object with function
arr = [0, 2, 3, -1, -5]
console.log(arr
.map(x => x * 2)
.filter(x => x > 0)
) // [ 4, 6 ]
// slice(begin, end) (end not included) // return a new array
arr = [0, 1, 2, 3, 4, 5, 6]
console.log(arr.slice(3)) // [ 3, 4, 5, 6 ]
console.log(arr.slice(3, 1)) // [] // doesn't exist
console.log(arr.slice(3, 4)) // [ 3 ] // from [first_i] to [second_i]
console.log(arr.slice(3, -1)) // [ 3, 4, 5 ] // through the second-to-last element
// or you can say through the end, but end is not included
console.log(arr)
// [
// 0, 1, 2, 3,
// 4, 5, 6
// ]
// splice(start, deleteCount, item) // change original array
let months = ['Jan', 'March', 'April', 'June']
months.splice(1, 0, 'Feb')
console.log(months)
// [ 'Jan', 'Feb', 'March', 'April', 'June' ]
// delete 0, means to insert item to index[1]
months.splice(4, 1, 'May')
console.log(months)
// [ 'Jan', 'Feb', 'March', 'April', 'May' ]
// delete 1 in index[4], and inset item to index[4]
// sort() // change original array
months = ['March', 'Jan', 'Feb', 'Dec']
months.sort()
console.log(months)
// [ 'Dec', 'Feb', 'Jan', 'March' ]
// sorting by alphabetical order
//
let arrNum = [1, 30, 4, 21]
arrNum.sort()
console.log(arrNum) // [ 1, 21, 30, 4 ]
//
// use sort(compareFunction)
// small to big
arrNum = [1, 30, 4, 21]
arrNum.sort(function (a, b) {
if (a === b) return 0 // do nothing
if (b > a) return -1 // change index
return 1
})
console.log(arrNum) // [ 1, 4, 21, 30 ]
//
// big to small
arrNum = [1, 30, 4, 21]
arrNum.sort(function (a, b) {
if (a === b) return 0
return a > b ? -1 : 1
})
console.log(arrNum) // [ 30, 21, 4, 1 ]
// note: if it was string or floating point, it would be more complicated
//
// other sorts: small to big
arrNum = [1, 30, 4, 21]
arrNum.sort(function (a, b) {
if (a === b) return 0
return (b - a) > 0 ? -1 : 1
})
console.log(arrNum)
Array methods:
map():のなかに直接function書いてもいいし、別のところで書いてmap()内に呼び出すか、とにかく配列のすべての要素に同じことを施すメソッドです。forと類似した性質を持っているので、自分の場合はインプットのタイプ型(String⇔Number)などの処理や複雑ではない処理をmap()に任しています。(新しい要素を返す、新しい要素で古い要素を書き換える)
複雑のループ、例えば多重ループにはforを使っています。
filter():名前通り、ろ過機能をもっていて、要素ごとに同じことを施す。filter()ができることmap()も同じくできていると、最初はそう思ってましたが、filter()は新しい配列を返す、map()は元の配列を変えるという違いが分かって根本的に違うことを認識しました。
よく使うString methods
// toString()
let a = 2
console.log(a.toString()) // 2
//
console.log(a + '') // 2
// toUpperCase()
let b = 'abc'.toUpperCase()
console.log(b) // ABC
// toLowerCase()
b = 'ABC'.toLowerCase()
console.log(b) // abc
b = 'ABC!!@#NSDAdsaaD'.toLowerCase()
console.log(b) // abc!!@#nsdadsaad
// charCodeAt() -> find ASCII code
b = 'ABC'
let bCode = b.charCodeAt(0)
console.log(bCode) // 65
bCode = b.charCodeAt(1)
console.log(bCode) // 66
//
b = 'abc'
bCode = b.charCodeAt(0)
console.log(bCode) // 97
bCode = b.charCodeAt(1)
console.log(bCode) // 98
// fromChatCode()
let str = String.fromCharCode(65)
console.log(str) // A
// Lower to Upper:
// A: 65, a:97, 97 - 65 = 32
let c = 'g'
let cCode = c.charCodeAt(0)
str = String.fromCharCode(cCode - 32)
console.log(str) // G
//
str = String.fromCharCode(cCode - ('a'.charCodeAt(0) - 'A'.charCodeAt(0)))
console.log(str) // G
//
// judge whether lowercase letter or not
let char = 'g'
console.log(char >= 'a' && char <= 'z') // true
// indexOf (return first index)
str = 'hello, world'
let indexStr = str.indexOf('hello')
console.log(indexStr) // 0
indexStr = str.indexOf('o')
console.log(indexStr) // 4
indexStr = str.indexOf('a')
console.log(indexStr) // -1
// replace (change first index) // change original array
str = 'hello, world'.replace('hello', 'HELLO')
console.log(str) // HELLO, world
str = 'hello, world'.replace('o', 'O')
console.log(str) // hellO, world
//
// note: use RegExp to change all the index
str = 'hello, world'.replace(/o/g, 'O') // g -> global
console.log(str) // hellO, wOrld
// split // return a new Array
console.log(str.split(''))
// [
// 'h', 'e', 'l', 'l',
// 'O', ',', ' ', 'w',
// 'O', 'r', 'l', 'd'
// ]
console.log(str.split(' ')) // [ 'hellO,', 'wOrld' ]
console.log(str.split('o')) // [ 'hellO, wOrld' ] // can't find 'o', but return a new array
console.log(str.split('O')) // [ 'hell', ', w', 'rld' ]
console.log(str.split(',')) // [ 'hellO', ' wOrld' ]
console.log(str) // hellO, wOrld
//
// note: usually used to process CSV
str = 'data1,data2,data3,data4'
console.log(str.split(',')) // [ 'data1', 'data2', 'data3', 'data4' ]
// trim()
str = ' data1,data2,data3,data4 '
console.log(str.trim()) // data1,data2,data3,data4
console.log(str) // data1,data2,data3,data4
// length
str = 'data1,data2,data3,data4'
console.log(str.length) // 23
for (let i = 0; i < str.length; i++) {
console.log(str[i])
}
String methods:基本的な文字列操作です。JSは正確の整数の桁数が15桁まで行けます。15桁超過すると数値が正確に扱うことができないということで、toString()など型変換して文字列として保管することができます。そこから文字列メソッドによる操作で区切って別々に処理を行うことも可能です。また、BigInt()で型変換して保管するのもできます。
よく使うNumber methods
// Number() & parseInt()
let a = 10
let b = '20'
console.log(a + b) // 1020
console.log(a + Number(b)) // 30
console.log(a + parseInt(b, 10)) // 30
console.log(a + parseInt(b, 12)) // 34
// parseFloat()
b = '20.35'
console.log(a + parseInt(b, 10)) // 30
console.log(a + parseFloat(b)) // 30.35
// toFixed() // String
b = '20.3586765'
console.log(parseFloat(b).toFixed(3)) // 20.359 // rounding // String
console.log(parseFloat(b).toFixed()) // 20 // String
// Number.MAX_VALUE
console.log(Number.MAX_VALUE) // 1.7976931348623157e+308
// Math.PI // constant
console.log(Math.PI) // 3.141592653589793
// Math.ceil
console.log(Math.ceil(10.5)) // 11 // up to the ceil
// Math.floor
console.log(Math.floor(10.5)) // 10 // down to the floor
// Math.rounding
console.log(Math.round(10.4)) // 10
console.log(Math.round(10.5)) // 11
// Math.sqrt
console.log(Math.sqrt(9)) // 3
// Math.pow
console.log(Math.pow(2, 10)) // 1024
// Math.random
console.log(Math.random()) // random 0~0.9999.., inclusive of 0. but not 1
console.log(Math.random() * 10) // random 0~9.9999...
console.log(Math.random() * 10 + 1) // random 0~10.9999
console.log(Math.floor(Math.random() * 10 + 1)) // random 0~10
Number methods:いろんな算数式メソッドをまとめましたが、ここで書いていない点を補足したいと思います。
文字列であっても、 *, /, % と一緒に使う場合は、強制的に数字に変換されます。
0.1+0.2≠0.3などfloating point(浮動小数点)をどう扱うかという問題もよく出てきます。parseInt()で十六進法に変換するか、もしくはtoFixed()を使って強制的に解決できるが、toFixed()値が文字列に変換されて、もう一度Number()かparseInt()でnumberに変換します。
let x = 0.1
let y = 0.2
console.log(x + y) // 0.30000000000000004
console.log(Number((x + y).toFixed(1)) === 0.3) // true
console.log(Number.MAX_SAFE_INTEGER) // 9007199254740991 // 2^53 -1
整数も小数位も17桁まで行けますが、16桁から不正確の数値が出てくるので正確の数値が欲しいなら15桁まで収めるといいですね。
ほかのメソッド補足
String.prototype.slice() & Array.prototype.slice()
// slice(start, end)
console.log('hello world'.slice(1)) // ello world
console.log('hello world'.slice(1, 5)) // ello
// use negative index => start at the end, back to the start
console.log('hello world'.slice(1, -1)) // ello worl
console.log('hello world'.slice(-1)) // d
console.log('hello world'.slice(-1, -2)) // *empty*
console.log('hello world'.slice(-2, -1)) // l
// use slice() in an array
console.log(['zero', 'one', 'two', 'three'].slice(-2, -1)) // [ 'two' ]
// return a new array, not change original array
slice() は要素の一部を切って新しい値として返すメソッドです。二つ目のパラメータ、endはこのインデックスの前まで切って、インデックス自身の要素を含まない。
また、元の配列を変えないというのは、下のsplice()と大きな違いがあります。
Array.prototype.splice()
// splice(start, deleteCount, replaceItem1, replaceItem2...)
// only use in array, change original array
// insert
const arr = ['zero', 'one', 'two', 'three']
arr.splice(1, 0, 'hello')
console.log(arr) // [ 'zero', 'hello', 'one', 'two', 'three' ]
// delete
arr.splice(1, 2) // start index 1, delete 2 elements
console.log(arr) // [ 'zero', 'two', 'three' ]
// replace
arr.splice(0, 1, 'world') // start index 0, delete 1 element, replace with 'world'
console.log(arr) // [ 'world', 'two', 'three' ]
arr.splice(1, 2, 'world') // delete 2 elements, replace 1 element
console.log(arr) // [ 'world', 'world' ]
arr.splice(1, 2, 'hello', 'world') // delete 1 element, replace 2 elements
console.log(arr) // [ 'world', 'hello', 'world' ]
splice() は配列の要素を追加したり削除したり、元の配列を変更するメソッドです。二つ目のパラメータが要素の削除に関連し、0を渡したら何も削除せず新しい要素を入れるようになります。1以上を渡したら削除したり、あるいは三つ目以上のパラメータで渡された別要素で入れ替えたりします。(配列のインデックスを越えた場合はinsertとなる。)
Array.prototype.reduce()
/*
reduce(callbackFn, initialValue)
reduce(
(previousValue, currentValue) => previousValue + currentValue,
initialValue
)
*/
let arr = [6, 5, 4, 3, 2, 1]
console.log(arr.reduce(
function (recentTotal, value) {
return recentTotal + value
}, 0
)) // 21
console.log(arr.reduce((recentTotal, value) =>
recentTotal + value, 0)) // 21
/*
recentTotal(initialValue) + arr[0] => 0 + 6 = 6
recentTotal(6) + arr[1] => 6 + 5 = 11
recentTotal(11) + arr[2] => 11 + 4 = 15
recentTotal(15) + arr[3] => 15 + 3 = 18
recentTotal(18) + arr[4] => 18 + 2 = 20
recentTotal(20) + arr[5] => 20 + 1 = 21
*/
console.log(arr.reduce((recentTotal, value) =>
recentTotal + value)) // 21
// note: if initialValue wasn't specified, it will be arr[0]
/*
arr[0] + arr[1] => 6 + 5 = 11
arr[1] + arr[2] => 11 + 4 = 15
...
*/
console.log(arr.reduce((recentTotal, value) =>
recentTotal + value, 10)) // 31
reduce() は配列に使うメソッド、一番使いやすいのはすべての要素の和を返す方法です。
Array.prototype.sort()
// sort()
// default sort is ascending order(昇べき), change original array
// judged by element's opening
const arr = [1, 50, 1000, 4, 3000]
console.log(arr.sort()) // [ 1, 1000, 3000, 4, 50 ]
console.log(arr) // [ 1, 1000, 3000, 4, 50 ]
// letter is judged by alphabetical order
const months = ['March', 'Jan', 'Feb', 'Dec']
console.log(months.sort()) // [ 'Dec', 'Feb', 'Jan', 'March' ]
console.log(months) // [ 'Dec', 'Feb', 'Jan', 'March' ]
// sort(compareFunction)
// make a sort function to judge
let numArr = [1, 50, 1000, 4, 3000]
// numArr.sort(function compareFunction(a, b) {
// if (a < b) return -1
// })
// console.log(numArr) // [ 1, 4, 50, 1000, 3000 ]
// write like this way, it also can make ascending order array
// because they both mean if (a < b) return negative number
// 1 - 4 = -3, 4 - 50 = -46..., etc.
numArr.sort((a, b) => a - b)
console.log(numArr) // [ 1, 4, 50, 1000, 3000 ]
// so when we write like this, it will make descending order array
// if (a > b) return positive number
// 3000 - 1000 = 2000, 1000 - 50 = 950..., etc.
numArr.sort((a, b) => b - a)
console.log(numArr) // [ 3000, 1000, 50, 4, 1 ]
numArr = [1, 50, 50, 4, 1]
console.log(numArr.sort((a, b) => a - b)) // [ 1, 1, 4, 50, 50 ]
console.log(numArr.sort((a, b) => b - a)) // [ 50, 50, 4, 1, 1 ]
/*
function compare(a, b) {
if (a < b) {
// ある順序の基準において a が b より小
return -1;
}
if (a > b) {
// その順序の基準において a が b より大
return 1;
}
// a は b と等しいはず
return 0;
}
*/
sort() は普通の数値型配列の並び替えなら一番お手軽に使えるメソッドだと思います。文字列でもできますが基本的にアルファベット順に従います。日本語の場合はUTF-16コードを参考して関数式を書かなければいけないです。
undefined 以外のすべての配列要素は文字列に変換され、文字列が UTF-16 コード単位順でソートされます。
debug - console.log
// Prime number has 2 divisors(factors) '1' and itself
// we want num to iterate itself from 2 to itself - 1
// it has some problem in 'else'
function isPrime(num) {
console.log('num:', num)
if (num === 1) return false
if (num === 2) return true
for (let i = 2; i < num; i++) {
console.log('i:', i)
if (num % i === 0) {
console.log('num & i === 0', 'num:', num, 'i:', i)
return false
}
}
return true
}
console.log(isPrime(15))
// num: 15
// i: 2
// i: 3
// num & i === 0 num: 15 i: 3
// false
debug:自分はまだまだ初心者なので、書いたコードを実行させたらどこにエラーが出てきたかわからない時、まずconsole.log()をいろんなところに入れて出力を読むのです。非効率なやり方かもしれませんが、プログラムの動きやロジックを一番直視できるやり方だと思います。
感想
JSを勉強し始めてちょうど五ヵ月になりました。最初はプログラミングに自信がなかったですが、分からないことを調べて、調べても分からないなら課題として残っていったん次のステップに進みます。本当は何も分かってないので不安しかありませんでしたけれど、つまずいてもしょうがないなあ凡人ですもんねって無理のない範囲で頑張ってきました。
そしたら知らないうちに簡単な練習もできるようになりました。自分のノートを振り返えながら自分の成長を実感しています。これからも頑張っていきたいと思います。
参考資料のまとめ
Falsy (偽値) - MDN Web Docs 用語集: ウェブ関連用語の定義
| MDN
JS Comparison Table
Array.prototype.sort() - JavaScript | MDN