1. Sum All Numbers in a Range
传入的参数是包含两个数字的数组,要求计算两数之间(包含边界)所有数字的和,较小的数字不一定在数组第一位;
function sumAll(arr) { var start = arr[0] ,end = arr[1]; return (start + end) * (Math.abs(end - start) + 1) / 2; }
sumAll([1, 5]); // 15
2. Diff Two Arrays
传入两个数组,要求求差,即返回一个新数组,新数组里的值只能在数组1或数组2中找到,不能同时出现在两个数组中;
function diffArray(arr1, arr2) { arr1 = arr1.slice(); // 为了不改变传入的数组的值,先对数组进行一次浅复制 arr2 = arr2.slice(); for (var i = arr1.length - 1; i >= 0; i--) { var index = arr2.indexOf(arr1[i]); // 寻找两个数组同时拥有的项,找到的话把这一项从两个数组中删掉 if (index > -1) { // 必须先把index存起来,因为后面使用splice()会改变数组,这时再动态查询index计算会出错误 arr1.splice(i, 1); // arr1去掉了一项,后面一项往前移,此时再用arr2.indexOf(arr1[i])得到的结果就是错的了 arr2.splice(index, 1); } } var newArr = arr1.concat(arr2); // 剩下来的就是两个数组中不同的值 return newArr; }
也可以先把两个数组合并起来,再使用类似数组去重的方法,但是找到的相同元素不是留一个,而是要全删掉;
function diffArray(arr1, arr2) { var newArr = arr1.concat(arr2); var re = []; newArr.forEach(ele => { var index = re.indexOf(ele); // 这里的index不用先保存也不会出错 if (index === -1) { // 如果结果数组中没有,就把这一项加进去 re.push(ele); } else { // 如果在结果数组中已存在,那就忽略这一项,并把在结果数组中存在的这一项也删掉,这里和数组去重不一样 re.splice(index, 1); } }); return re; }
3. Seek and Destroy
传入一个数组和若干个其他参数,要求删除数组中与其他参数相等的项;
function destroyer(arr) { var removedVal = Array.prototype.slice.call(arguments, 1); var re = arr.filter(ele => removedVal.indexOf(ele) === -1); return re; } destroyer([1, 2, 3, 1, 2, 3], 2, 3); // [1, 1]
4. Wherefore art thou
写一个函数,这个函数接收两个参数,第一个参数是成员都是对象的数组,第二个参数是一个对象;
要求遍历对象数组,如果对象成员包含与第二个参数所有键值对相同的键值对,就返回这个对象,最后返回符合要求的对象数组;
也就是说,如果第一个参数是[{name: 'suki', age: 21, gender: 'female'}, {name: 'cora', age: 22}, {name: 'suki'}],第二个参数是{name: 'suki', age: 21},最后的返回结果是[{name: 'suki', age: 21, gender: 'female'}];
换句话说,返回结果是一个新数组,由第一个参数中符合条件的对象组成,条件是第二个参数是这个对象的一个子集;
function whatIsInAName(collection, source) { var arr = []; var sourceKeys = Object.keys(source); arr = collection.filter(obj => sourceKeys.every(key => source[key] === obj[key])); return arr; } whatIsInAName([{ first: "Romeo", last: "Montague" }, { first: "Mercutio", last: null }, { first: "Tybalt", last: "Capulet" }], { last: "Capulet" });
// [{ first: "Tybalt", last: "Capulet" }]
5. Spinal Tap Case
把字符串变成'aaa-bbb-ccc-ddd'这种格式,作为参数的字符串可能'thisIsSpinalTap' 这样的,也可能是'This Is<abbr>Spinal_Tap'这样的;虽然我的解法通过了测试,但自己觉得有点繁琐二球难看,如果你知道更简单的方法的话欢迎留言~
function spinalCase(str) { var reg1 = /[^\w\s'].*[^\w\s']/g; // 匹配像<abbr>这种分隔符 str = str.replace(reg1, '-'); // 用短横线先做个记号分隔单词 var reg2 = /([A-Z])/g; // 有些单词之间没有分隔符,只靠后一个单词首字母大写来区分两个单词 str = str.replace(reg2, ($1) => '-' + $1.toLowerCase()); // 用短横线做个记号分隔单词并把大写字母转成小写字母 var reg3 = /[^a-zA-Z0-9']+/g; str = str.replace(reg3, '-'); return str.replace(/^-/, ''); }
6. Pig Latin
把英语单词转换成pig latin,也就是把单词开头的辅音字母(一个或连续多个)移到单词尾部再加上后缀'ay',如果单词不以辅音字母开头,直接在词尾加'way';
传进来的参数都是小写的英文单词;
function translatePigLatin(str) { var consonants = /^[^aeiou]+/; if (str.match(consonants) !== null) { return RegExp.rightContext + RegExp.lastMatch + 'ay'; } else { return str + 'way'; } } translatePigLatin("consonant"); // 'onsonantcay'
7. Search and Replace
函数接收三个字符串参数(str, before, after),要求在str中找到before,然后用after替换掉它;
after的大小写要与before一致,如果before是'Dog',after是'cat',那么替换结果是'Cat';
function myReplace(str, before, after) { var reg = new RegExp(before); return str.replace(reg, match => { if (/^[A-Z]/.test(match)) { return after.replace(after[0], after[0].toUpperCase()); } return after; }); } myReplace("A quick brown fox jumped over the lazy dog", "jumped", "leaped"); // "A quick brown fox leaped over the lazy dog"
8. DNA Pairing
传入一个DNA序列字符串,如'GCG',要求给DNA序列配对后,返回一个二维数组;
配对规则是'A'配'T','C'配'G';
返回的数组中的每一项是一对配对的DNA,如[["G", "C"], ["C","G"],["G", "C"]],传入的DNA字符串中的元素放在前面;
function pairElement(str) { var re = []; var strand = str.split(''); var pairing = new Map([['G', 'C'], ['C', 'G'], ['A', 'T'], ['T', 'A']]); strand.forEach(ele => { re.push([ele, pairing.get(ele)]); }); return re; } pairElement("GCG"); // [["G", "C"], ["C","G"],["G", "C"]]
9. Missing lette
接收一个字符串参数,要求找出字符串中缺失的字母并返回,如传入'abce',需要返回'd';如果传入的字符串没有缺失的字母,返回undefined;
function fearNotLetter(str) { var arr = str.split('').map(char => char.charCodeAt(0)); var missing; for (var i = arr.length - 1; i >= 0; i--) { if (arr[i] - arr[i - 1] > 1) { missing = arr[i] - 1; break; } if (i === 0) { return undefined; } } return String.fromCharCode(missing); } fearNotLetter("abce"); // 'd'
10. Sorted Union
要求写一个函数,函数接收两个或以上数组作为参数,对这些数组进行去重后返回一个新数组,举个例子,传入参数为([1, 3, 2], [5, 2, 1, 4], [2, 1]),要求返回[1, 3, 2, 5, 4]
,数组里的数字的排列顺序跟它们在原数组的顺序一样;我的思路就是把所有传进来的数组合成一个新数组再去重;
function uniteUnique(...arrs) { var arrUnion = arrs.reduce((a, b) => a.concat(b)); var re = new Set(arrUnion); return [...re]; } uniteUnique([1, 3, 2], [5, 2, 1, 4], [2, 1]); // [1, 3, 2, 5, 4]
11. Convert HTML Entities
把传入的字符串中不符合HTML语法的字符进行转义,即对&, <, >, ", ' 进行转义;
function convertHTML(str) { var convertRules = { '&': '&', '<': '<', '>': '>', '"': '"', '\'': ''' }; return str.replace(/[&<>"']/g, match => convertRules[match]); } convertHTML("Dolce & Gabbana"); // "Dolce & Gabbana"
12. Sum All Odd Fibonacci Numbers
传入一个正整数,返回一个斐波那契数列中小于等于这个正整数的奇数的和;
斐波那契数列开始两项分别是1和1,之后每一项都是前面两项的和;
我的思路是先把这个斐波那契数列中小于参数的项存在一个数组中,再遍历这个数组,求所有奇数的和;
function sumFibs(num) { var fibNums = [1, 1]; for (var i = 0; i <= fibNums.length; i++) { fibNums[i] = fibNums[i] ? fibNums[i] : (fibNums[i-1] + fibNums[i-2]); if (fibNums[i] > num) { fibNums.pop(); break; } } var re = fibNums.reduce((a, b) => (b % 2 === 0) ? a : a + b, 0); return re; } sumFibs(4); // 5
13. Sum All Primes
传入一个正整数,返回小于等于这个正整数的所有质数的和;
function sumPrimes(num) { function isPrime(n) { for (let i = 2; i <= n / 2; i++) { if (n % i === 0) { return false; } } return true; } var re = 0; for (let i = 2; i <= num; i++) { if (isPrime(i)) { re += i; } } return re; } sumPrimes(10); // 17
14. Smallest Common Multiple
传入一个数组,数组中有两个数字,要求求这两个个数字范围内的所有数字的最小公倍数;
例如,传入的数组是[1, 5],那么需要求得1, 2, 3, 4, 5这几个数字的最小公倍数,即60;
传入的数组中的数字不一定是升序排列的,也可能传入[5, 1];
我的思路是先填充一个新数组,得到参数数组范围内的所有数字;
要求得若干个数字的最小公倍数,先求两个数的最小公倍数,再求这个最小公倍数与第三个数的最小公倍数,重复操作即可得到结果;
求两个数的最小公倍数时,我先使用更相减损法求得两数的最大公约数,再用公式求两数的最小公倍数;
function smallestCommons(arr) { // 使用arr范围内的数字填充新数组 var newArr = []; var start, end; if (arr[0] - arr[1] < 0) { start = arr[0]; end = arr[1]; } else { start = arr[1]; end = arr[0]; } for (let i = start; i <= end; i++) { newArr.push(i); } // 求数组所有项的最小公倍数 var re = newArr.reduce((a, b) => { var num1 = a ,num2 = b; // 先求两个数的最大公约数 // 第一步:如果两个数都是偶数,先约简 // 否则直接开始第二步 var multiply2 = 1; while ((num1 % 2 === 0) & (num2 % 2 === 0)) { num1 /= 2; num2 /= 2; multiply2 *= 2; } // 第二步:更相减损法求最大公约数 // 以大数减小数,得到的等数与减数比较,再用较大数减较小数 // 重复上面的操作,直到得到的等数与减数相等 var bigNum, smallNum, diff; if (num1 > num2) { bigNum = num1; smallNum = num2; } else { bigNum = num2; smallNum = num1; } diff = bigNum - smallNum; while (diff !== smallNum) { if (smallNum > diff) { bigNum = smallNum; smallNum = diff; } else { bigNum = diff; } diff = bigNum - smallNum; } // 第一步中约简掉的'2'与第二步中得到的等数的乘积就是两个数的最大公约数 var biggestCommonDivisor = diff * multiply2; // 求最小公倍数 // 两个数的乘积等于两数最大公约数与最小公倍数的乘积 var smallestCommonMultiple = a * b / biggestCommonDivisor; // 得到的最小公倍数再与下一个数求最小公倍数 // 重复操作即可得到若干个数的最小公倍数 return smallestCommonMultiple; }); return re; } smallestCommons([1,5]); // 60
15. Drop it
函数接收两个参数,一个数组(arr),一个函数(func);
要求从索引0开始遍历数组(arr),把数组项作为参数传给函数(func),如果函数(func)返回false,则去掉这一项,直到数组项满足函数(func)的要求返回true,停止遍历,返回余下的数组部分;
function dropElements(arr, func) { while (!func(arr[0])) { arr.shift(); } return arr; } dropElements([1, 2, 3], function(n) {return n < 3; }); // [3, 4]
16. Steamroller
传入一个多维数组,要求把它完全展开;
我的思路是,给函数增加一个参数re用于存储展开后的数组项,它的默认值是一个空数组;
在函数中,遍历多维数组,对每一个数组项进行判断,如果数组项不是数组,把这一项推到re中,如果数组项是数组,那就再调用steamrollArray函数,把这一项和re作为参数传进函数;遍历完成后返回结果;
function steamrollArray(arr, re=[]) { arr.forEach(ele => { if (!Array.isArray(ele)) { re.push(ele); } else { steamrollArray(ele, re); } }); return re; } steamrollArray([1, [2], [3, [[4]]]]); // [1, 2, 3, 4]
17. Binary Agents
传入一个二进制数字构成的字符串,要求把这个字符串翻译成英文,二进制数字之间以空格隔开;
function binaryAgent(str) { var arr = str.split(' ').map(ele => { // 把每个二进制数字字符串转换成十进制数字,再转成英文字母 return String.fromCharCode(parseInt(ele, 2)); }); return arr.join(''); } binaryAgent("01000001 01110010 01100101 01101110 00100111 01110100 00100000 01100010 01101111 01101110 01100110 01101001 01110010 01100101 01110011 00100000 01100110 01110101 01101110 00100001 00111111");
// "Aren't bonfires fun!?"
18. Everything Be True
传入两个参数,第一个是由对象组成的数组,第二个是一个对象属性字符串,如果在数组的每一个对象中,与第二个参数同名的属性都为真值,函数返回true,否则返回false;
function truthCheck(collection, pre) { return collection.every(obj => !!obj[pre]); // 我在这里使用了!!是因为觉得这样比较好理解,去掉结果也是一样的 } truthCheck([{"user": "Tinky-Winky", "sex": "male"}, {"user": "Dipsy", "sex": "male"}, {"user": "Laa-Laa", "sex": "female"}, {"user": "Po", "sex": "female"}], "sex");
// true
19. Arguments Optional
写一个函数,这个函数接收一个或两个参数,如果传入两个参数(a, b),返回两个参数的和;
如果传入一个参数(a),返回一个接收一个参数(b)的函数,该函数返回外部函数参数a和自身参数b的和;
如果任意一个参数不是数字,函数返回undefined;
function addTogether(...arg) { if (arg.every(ele => (typeof ele) == 'number')) { return arg.length === 2 ? arg[0] + arg[1] : function(n) { return (typeof n) == 'number' ? arg[0] + n : undefined; }; } else { return undefined } } var sumTwoAnd = addTogether(2); sumTwoAnd(3); // 5
20. Make a Person
有一个Person构造函数,需要在这个构造函数中增加以下方法:
getFirstName(),getLastName(),getFullName(),setFirstName(first),setLastName(last),setFullName(firstAndLast)
其中那些接收一个参数的方法必须接收一个字符串参数;要求这些方法是唯一能与对象交互的方法;
var Person = function(firstAndLast) { // 定义私有变量 var firstName = firstAndLast.split(' ')[0] ,lastName = firstAndLast.split(' ')[1]; this.getFirstName = function() { return firstName; }; this.getLastName = function() { return lastName; }; this.getFullName = function() { return this.getFirstName() + ' ' + this.getLastName(); }; this.setFirstName = function(first) { firstName = first; }; this.setLastName = function(last) { lastName = last; }; this.setFullName = function(firstAndLast) { firstName = firstAndLast.split(' ')[0] ,lastName = firstAndLast.split(' ')[1] } }; var bob = new Person('Bob Ross'); bob.getFullName(); // 'Bob Ross' bob.setFirstName('Haskell'); bob.getFullName(); // 'Haskell Ross'