这部分没有具体的知识点,全都以习题的形式来巩固之前的内容。这里我直接将题目和解答思路放上来:
Introduction to the Intermediate Algorithm Scripting Challenges
Sum All Numbers in a Range 1. 求区间和
题干: 第一题给定一个数组作为参数,数组中包含两个数字(大小顺序不定),需要求出这两个数字之间所有数字的和。我写了两个解法:
第一个解法用
for
循环,循环出两个数字之间所有的数,让他们累加到result
上。这里用到了
Math.min(num1, num2)
和Max.max(num1, num2)
.1
2
3
4
5
6
7
8
9function sumAll(arr) {
let result = 0;
for(let i = Math.min(arr[0], arr[1]); i <= Math.max(arr[0], arr[1]); i ++) {
result += i;
}
return result;
}
sumAll([1, 4]);第二个方法是小学学过的等差数列求合法,我还记得口诀:首项加末项的和,乘以项数除以2。这里需要注意项数的求法,首项减末项的绝对值加1
1
2
3
4
5function sumAll(arr) {
return (arr[0]+arr[1])*(Math.abs(arr[0]-arr[1])+1)/2;
}
sumAll([1, 4]);Diff Two Arrays 2. 对比数组差别
题干: 这题需要比较两个数组,返回一个新的数组,里面的元素只在其中一个数组出现过。
这题本来看到重复第一反应是利用
set
来做,但是其实是只返回出现了一次的,也就是说重复的元素两个都需要去掉,所以set
不太有用。我采用的方法是两个数组分别筛选出另一个数组里没有的元素,然后添加到新数组中。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16function diffArray(arr1, arr2) {
let newArr = [];
arr1.filter(ele => {
if(arr2.indexOf(ele) === -1){
newArr.push(ele);
}
});
arr2.filter(ele => {
if(arr1.indexOf(ele) === -1){
newArr.push(ele);
}
});
return newArr;
}
diffArray([1, 2, 3, 5], [1, 2, 3, 4, 5]);-
题干:这一题同样考察筛选排除。参数给定一个数组和不确定数量的数字,要筛选出数组里和给定数字不相等的元素,返回数组。这里很容易就想到了
filter
,但是怎么用filter
,因为如果单纯用===
去比较太麻烦了,而且不知道到底笔记几位,所以我想到的是把剩余的参数全部放进新数组里,比较方便的做法就是解构赋值(ES6的很多语法真的都非常好用),然后indexOf
判断一下就OK了。需要注意的点是用
args
,不要有arguments
,因为在Chrome中,arguments
是保留关键字,无法直接用。1
2
3
4
5
6function destroyer(...args) {
let [, ...newArr] = args;
return args[0].filter(ele => newArr.indexOf(ele) === -1);
}
destroyer([1, 2, 3, 1, 2, 3], 2, 3); -
题干:参数
collection
是一个包含若干对象的数组,参数source
是一个对象,需要找出collection
中包含了source
对象中的值且值与source
相等的对象,返回这些对象的集合数组这题首先会想到需要遍历
collection
中的每个对象,然后需要想到如何比较两个对象,所以我把比较对象的方法compare
先单独存成一个变量,而且给定了一个参数默认值props
,遍历props
中的属性,比较collection
中每个对象是否包含这个属性且属性值与source
中一样,这里不必先判断hasOwnProperty
,直接比较久可以了。最后查看不相等的值的个数。1
2
3
4
5
6
7
8function whatIsInAName(collection, source) {
let compare = function(obj, arr = Object.keys(source)){
return arr.filter(prop => obj[prop] !== source[prop]);
}
return collection.filter(obj => compare(obj).length === 0);
}
whatIsInAName([{ first: "Romeo", last: "Montague" }, { first: "Mercutio", last: null }, { first: "Tybalt", last: "Capulet" }], { last: "Capulet" }); -
题干: 这题的要求是给定一个字符串,字符串由几个单词混合组成,连接单词的可能是空格,可能是
_
,也可能单词混合在一起,区分方法的首字母大写,要把这一组单词转换成由-
连接的全是小写格式的字符串。这题麻烦的地方是需要考虑的情况比较多,可以从结果出发,我们其实需要做的是辨认出每个单词,由横线连接
array.join('-')
,然后转换成小写string.toLowerCase()
,所以我们将单词区分出来放进一个数组就可以了,我的做法是:- 先解决单词分开的情况,如果单词由空格或者
_
来连接,即不是单词和单词连接在一起的情况,我们用/[^A-Za-z]/
来表示,然后以这个字符为标志切分字符串str.split(/[^A-Za-z]/)
,这样会返回两种结果,一个是["a", "b", "c"]
的数组,数组里面的每个元素不一定正好就是一个单独的单词,有可能是两个单词连在一起,后一个单词首字母大写的情况;还有一种是["abc"]
,即单词没有区分开 - 接下来我们需要对切分获得的数组进行遍历,让里面的每个元素都执行一个函数,这个函数让他们能把连在一起的单词切分开,这里用到的正则是
/[A-Z]?[a-z]+/g
,表示第一个字母可能是大写,也可能是小写(句首单词可能都是小写),之后都是小写,这样可以遇到首字母大写的单词就切开,这样就返回一个二维数组。 - 分别对两个数组进行在
-
连接,如果数组中只有一个元素,那么连接不起作用,正好可以在不多用-
的情况下把单词连接起来
1
2
3
4
5
6
7function spinalCase(str) {
let arr = str.split(/[^A-Za-z]/);
let re = /[A-Z]?[a-z]+/g;
return arr.map(val => val.match(re).join("-")).join("-").toLowerCase();
}
spinalCase('This Is Spinal Tap'); - 先解决单词分开的情况,如果单词由空格或者
-
题干:这题的要求是给定一个参数字符串,如果参数字符串以元音字母开头,则直接在字符串末尾添加
"way"
,如果不是以元音字母开头,则将第一个元音字母之前的辅音字母挪到末尾,再添加"ay"
。这里我用了一个很简单的正则判断元音字母,
/[aeiou]/
,然后找出第一个元音字母的位置,如果索引是0,
即单词元音字母开头,则str+="way"
,如果不是元音字母开头,则去掉开头的辅音字母str.slice(index)
,再把辅音字母挪到末尾+str.slice(0,index)
,再加上"ay"
1
2
3
4
5
6
7
8
9
10
11
12function translatePigLatin(str) {
let re = /[aeiou]/;
let index = str.match(re)===null? null:str.match(re).index;
if(index === 0) {
str += 'way';
} else {
str = str.slice(index) + str.slice(0, index) + "ay";
}
return str;
}
translatePigLatin("consonant"); -
题干: 这题给定三个参数,第一个是一个字符串句子,第二、三个都是单词,需要在字符串句子中找到第二个参数,并替换成第三个
这题其实很简单,核心就是
string.replace(word1, word2)
,唯一需要注意的是判断word1
和word2
是否首字母大写,这里用before[0] === before[0].toUpperCase()
来判断就可以了,如果原单词都是小写,替换的单词就都是小写(没有改变),如果原单词首字母大写,替换的单词就首字母大写after = after[0].toUpperCase() + after.slice(1)
1
2
3
4
5
6
7
8function myReplace(str, before, after) {
if(before[0] === before[0].toUpperCase()) {
after = after[0].toUpperCase() + after.slice(1);
}
return str.replace(before,after);
}
myReplace("A quick brown fox jumped over the lazy dog", "jumped", "leaped"); -
题干: 给定一个字符串,字符串只包含”C”, “G”, “A”, “T”四个字母,其中,[“C”, “G”]和[“A”, “T”]是两对基因,要求根据字符串的每个字母,找到对应的基因对,返回数组,里面每个元素都是对应的基因对,且基因对的第一个元素是字符串中的字母。
这题有一个有点坑的点,我最开始想的是保存两个变量
pair1
和pair2
,遍历字符串,让pair1.indexOf(string)
,找到字母对应的基因对,如果index
是0就返回这个数组,如果不是0就返回pair.reverse()
,但是reverse()
方法是直接修改数组的顺序,所以会导致最后输出的数组顺序都是一样的。最终的解决方案是把4种可能的基因对都放在一个数组里,遍历字符串的每个字母,去执行寻找功能,再遍历基因对的每一个数组,看看字母在哪个数组中的索引值是0,就在
result
数组中push
这个数组。1
2
3
4
5
6
7
8
9
10
11
12
13
14function pairElement(str) {
let pairs = [["C", "G"], ["G", "C"], ["A", "T"], ["T", "A"]];
let result = [];
str.split("").map(val => {
pairs.forEach(arr => {
if(arr.indexOf(val) === 0) {
result.push(arr);
}
})
})
return result;
}
pairElement("GCG"); -
题干:参数是一串按顺序排列的字符串,需要判断中间缺少了哪个字母,需要返回这个字母,如果不缺就返回
undefined
相邻字母的Unicode编码差1,利用这一点可以比较每个字母比后一个字母的编码是不是正好少1,如果不是,那这个字母之后就缺了一个字母,用
String.fromCharCode
可以获得这个字母1
2
3
4
5
6
7
8
9
10function fearNotLetter(str) {
for(let i = 0; i < str.length; i ++) {
if(str.charCodeAt(i) - str.charCodeAt(i + 1) < -1) {
return String.fromCharCode(str.charCodeAt(i) + 1);
}
}
return undefined;
}
fearNotLetter("abce"); -
题干: 参数是不定个数个数组,需要将这些数组组合起来,返回一个没有重复元素且顺序按照参数先后顺序的数组。
一看到数组去重,现在会条件反射想到
Set
,因为比其他方法方便太多。这里我用变量all
存储所有参数数组中的元素,然后利用new Set(all)
来获得一个没有重复元素的数据Set
,再利用Array.from(set)
来将元素转换成数组1
2
3
4
5
6
7function uniteUnique(...args) {
let all = [];
[...args].map(arr => all=all.concat(arr));
return Array.from(new Set(all));
}
uniteUnique([1, 3, 2], [5, 2, 1, 4], [2, 1]); -
题干:转换HTML转义符
这题的思路是创建一个对象保存转义符,这样切换的时候可以统一使用
string.replace(ele, entities[ele])
的方法。然后使用正则把需要转义的内容筛选出来就可以了。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15function convertHTML(str) {
let entities = {
"<" : "<",
">" : ">",
"&" : "&",
"'" : "'",
'"' : '"'
}
let re = /[^A-Za-z\s]/g
let arr = str.match(re);
arr===null? str = str: arr.map(ele => str = str.replace(ele, entities[ele]));
return str;
}
convertHTML("Dolce & Gabbana"); -
题干:这题需要求给定小于参数
num
的斐波那契数列中所有奇数的和。首先我们需要知道什么是斐波那契数列,从
1, 1
开始,后面每一个数字是前两个数字的和,1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17function sumFibs(num) {
let fibs = [1, 1];
let oddSum = 2;
while(true) {
let item = fibs[0] + fibs[1];
if(num < item) {
return oddSum;
}
if(item % 2) {
oddSum += item;
}
fibs[0] = fibs[1];
fibs[1] = item;
}
}
sumFibs(4); -
题干:求小于参数
num
的所有质数的和这题先确定怎么判断质数,然后再求和
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19function sumPrimes(num) {
let isPrime = function(num) {
for(let i = 2; i < num; i ++) {
if(num % i === 0) {
return false;
}
}
return true;
}
let result = 0;
for(let j = 2; j <= num; j ++) {
if(isPrime(j)){
result+=j;
}
}
return result;
}
sumPrimes(10); -
题干:求最小公倍数
最大公倍数的求法是两个数相乘除以最大公约数,所以要先求出最大公约数。然后求积。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18function smallestCommons(arr) {
function gcd(a, b){
if(a % b === 0){
return b;
}
return gcd(b, a % b);
}
var max = Math.max.apply(null, arr);
var min = Math.min.apply(null, arr);
var result = min;
for(var i = min + 1; i <= max; i ++){
result *= i / gcd(i, result);
}
return result;
}
smallestCommons([1,5]); -
题干:找出参数
arr
中第一个符合参数func
条件的元素,并截取数组arr
中自这个元素起的剩余部分我的解决方案是遍历数组
arr.map
,找出符合func
条件的所有元素,并在indice
数组中记录它们的索引值,sort()
排序后取第一个值,利用数组的array.slice
方法来截取数组1
2
3
4
5
6
7
8
9
10
11
12
13
14
15function dropElements(arr, func) {
let result= [];
let indice = [];
arr.map(val => {
if(func(val) === true) {
indice.push(arr.indexOf(val));
}
})
if(indice.length > 0) {
result = arr.slice(indice.sort()[0])
}
return result;
}
dropElements([1, 2, 3], function(n) {return n < 3; }); -
题干:这题的参数是一个多维数组,需要将里面的元素解析到一个一维数组中,作为结果返回
像这类判断数据类型然后可能需要反复做同一个转换操作的内容肯定会用到递归思想,这里就是判断数组内的元素是不是数组,如果是数组就再解析,不是数组就直接
push
到结果内。需要注意的是如何判断一个元素是不是数组,我刚开始想到的是用
typeof
,但是马上就会发现其实有问题,因为typeof
不适合判断复杂类型的数据,所以这里用到Object.prototype.toString.call(val)
,结果也和typeof
的结果略有不同,这里会返回一个数组转成的字符串[object Array]
1
2
3
4
5
6
7
8
9
10
11
12
13function steamrollArray(arr) {
let result = [];
arr.map(val => {
if(Object.prototype.toString.call(val) === "[object Array]") {
result = result.concat(steamrollArray(val));
} else {
result.push(val);
}
})
return result;
}
steamrollArray([1, [2], [3, [[4]]]]); -
题干:将二进制参数转换成字符串
核心知识点是
parseInt(num, 2)
将二进制编码转换成十进制数,和String.fromCharCode(num)
将Unicode编码转换成字符串。用string.split
将字符串切分成数组再遍历就可以了。1
2
3
4
5
6
7function binaryAgent(str) {
let result = "";
str.split(" ").map(val => result += String.fromCharCode(parseInt(val, 2)));
return result;
}
binaryAgent("01000001 01110010 01100101 01101110 00100111 01110100 00100000 01100010 01101111 01101110 01100110 01101001 01110010 01100101 01110011 00100000 01100110 01110101 01101110 00100001 00111111"); -
题干:判断参数
collection
数组中是否都有pre
这个属性,以及这个属性的值是否都为真像这类判断每个值的题,会很容易想到用
map
遍历,但是这样其实还需要再判断遍历之后返回的值,比较麻烦,如果用filter
来筛选出不符合要求的元素,并且判断包含这些元素的数组的长度,如果长度为0则表示所有的元素都符合要求,如果不为0则表示有元素不符合,不需要判断具体长度,这样会方便一些。1
2
3
4
5
6
7
8function truthCheck(collection, pre) {
if(collection.filter(val=> Boolean(val[pre]) === false).length === 0){
return true;
}
return false;
}
truthCheck([{"user": "Tinky-Winky", "sex": "male"}, {"user": "Dipsy", "sex": "male"}, {"user": "Laa-Laa", "sex": "female"}, {"user": "Po", "sex": "female"}], "sex"); -
题干:给定不定数量的参数,如果判断类型,如果是数字就求和。
这一题需要把情况列出来,我先判断了两个参数的情况,如果两个参数都是数字,直接求和,如果两个参数,不是全是数字,则返回
undefined
,如果只有一个参数,且是数字,则返回一个function
,再判断这个新方法的新参数及求和1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17function addTogether(...args) {
if([...args].length === 2 && typeof [...args][0] === "number" && typeof [...args][1] === "number") {
return args[0] + args[1];
} else if ([...args].length === 2) {
return undefined;
} else if ([...args].length === 1 && typeof [...args][0] === "number") {
return function(arg) {
if(typeof arg !== "number") {
return undefined;
} else {
return args[0] + arg;
}
}
}
}
addTogether(2,3); -
这一题考察对象的属性和方法,没什么难点,直接设定和获取就好了
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26var Person = function(firstAndLast) {
// Complete the method below and implement the others similarly
let name = firstAndLast.split(" ");
this.getFullName = function() {
return name.join(" ");
};
this.getFirstName = function() {
return name[0];
}
this.getLastName = function() {
return name[1];
}
this.setFirstName = function(first) {
name[0]= first;
}
this.setLastName = function(last) {
name[1]= last;
}
this.setFullName = function(firstAndLast) {
name = firstAndLast.split(" ");
}
return this;
};
var bob = new Person('Bob Ross');
bob.getFullName(); -
这一题唯一需要弄清楚的就是怎么计算这个公转周期。之后利用公式,也没有什么难度。
1
2
3
4
5
6
7
8
9
10
11
12
13function orbitalPeriod(arr) {
var GM = 398600.4418;
var earthRadius = 6367.4447;
let result = [];
arr.map(val => {
val.orbitalPeriod=Math.round(Math.sqrt(Math.pow((val.avgAlt+earthRadius), 3)/GM)*2*Math.PI);
val = {name: val.name, orbitalPeriod: val.orbitalPeriod};
result.push(val)
})
return result;
}
orbitalPeriod([{name : "sputnik", avgAlt : 35873.5553}]);