1.Validate US Telephone Numbers
如果传入字符串是一个有效的美国电话号码,则返回 true
.
用户可以在表单中填入一个任意有效美国电话号码. 下面是一些有效号码的例子(还有下面测试时用到的一些变体写法):
555-555-5555
(555)555-5555
(555) 555-5555
555 555 5555
5555555555
1 555 555 5555
在本节中你会看见如 800-692-7753
or 8oo-six427676;laskdjf
这样的字符串. 你的任务就是验证前面给出的字符串是否是有效的美国电话号码. 区号是必须有的. 如果字符串中给出了国家代码, 你必须验证其是 1
. 如果号码有效就返回 true
; 否则返回 false
.
function telephoneCheck(str) { //正则表达式 //^1?表示以1开头,1匹配0次或1次 //\d{3}匹配一个0-9的数字三次 //\(\d{3}\)匹配(一个0-9的数字三次),比上面多一个括号,左右括号分别需要加上转义字符\ //\s?表示空白字符匹配0次或1次 //[ -]?表示空格或者连字符-匹配0次或1次 //\d{4}$表示已4位数字结尾($) var re=/^1?\s?(\d{3}|\(\d{3}\))[ -]?\d{3}[ -]?\d{4}$/; return re.test(str); } telephoneCheck("1 555-555-5555");
2.Symmetric Difference
创建一个函数,接受两个或多个数组,返回所给数组的 对等差分(symmetric difference) (△
or ⊕
)数组.
给出两个集合 (如集合 A = {1, 2, 3}
和集合 B = {2, 3, 4}
), 而数学术语 "对等差分" 的集合就是指由所有只在两个集合其中之一的元素组成的集合(A △ B = C = {1, 4}
). 对于传入的额外集合 (如 D = {2, 3}
), 你应该安装前面原则求前两个集合的结果与新集合的对等差分集合 (C △ D = {1, 4} △ {2, 3} = {1, 2, 3, 4}
).
思路
问:怎么把大象装进冰箱里?第一步,把冰箱门打开。那么怎么解决这道题目?第一步,拿到所有可能传入的作为参数的待处理数组。
一看到那些未显示声明或者不确定个数的参数,就能想到要好好利用一下 arguments
这个ECMAScript中的特殊对象了。
在函数代码中,使用特殊对象 arguments,开发者无需明确指出参数名,就能访问它们。
接下来,把大象装进冰箱里,哦,不,是把 arguments
对象的集合放进一个新数组。
有两种方法可以办到。第一种,老生常谈的 for()
循环。
var arr = [];
for(var i = 0; i < arguments.length; i++){ arr.push(arguments[i]); }
第二种是使用 ECMA-262 第六版标准添加的 Array.from()
方法。它可以将两类对象转为真正的数组: 类似数组的对象(array-like object) 和 可遍历(iterable)的对象。
var arr = Array.from(arguments);
通读题目,其实就是要求我们把参数数组中的前两项做运算,得到一个所有项都没同时出现在两个数组中的新数组,然后把这个新数组作为首项,继续与后面的数组进行同样操作。
我们第一步得到的参数数组是个二维数组,而函数最后返回的是一个结果,一维数组,很容易就想到Array归并用的 reduce()
方法。
reduce()
传入的函数带有4个参数:前一个值 prev
、当前值 cur
、项的索引 index
、数组对象 array
。
分别对 prev
和 cur
进行过滤,返回对方数组中不存在的值,连接成为新数组。最后对新数组去重。
去重时,可以另写一个函数,像这样:
function unique(array){
var n = []; for(var i = 0;i < array.length; i++){ if(n.indexOf(array[i]) == -1) { n.push(array[i]); } } return n; }
也可以在返回结果中继续利用 filter()
去重:
//整个函数的返回值
return temp.filter(function(item,index,array){ return array.indexOf(item) == index; });
function sym(args) { var arr = []; for(var i = 0; i < arguments.length; i++){ arr.push(arguments[i]); } //var arr = Array.from(arguments); var temp = arr.reduce(function(prev,cur,index,array){ var a = prev.filter(function(item){ return cur.indexOf(item) < 0; }); var b = cur.filter(function(item){ return prev.indexOf(item) < 0; }); return a.concat(b); }); return temp.filter(function(item,index,array){ return array.indexOf(item) == index; }); //或者调用外部函数去重;function unique(array)见“思路”部分 //return unique(temp); }
3.Exact Change
设计一个收银程序 checkCashRegister()
,其把购买价格(price
)作为第一个参数 , 付款金额 (cash
)作为第二个参数, 和收银机中零钱 (cid
) 作为第三个参数.
cid
是一个二维数组,存着当前可用的找零.
当收银机中的钱不够找零时返回字符串"Insufficient Funds"
. 如果正好则返回字符串"Closed"
.
否则, 返回应找回的零钱列表,且由大到小存在二维数组中.
思路:这道题其实不难,就是异常的麻烦。感觉题目有给一定的提示,就是它注释起来那个array.因为涉及都美分,所以要乘以一个基数100来简化运算。再一个,常规思维,如果找零的时候,有1张20,肯定不会去用2张10元的。
1 //创建一个面额的对象数组 2 var denom = [ 3 { name: 'ONE HUNDRED', val: 100.00}, 4 { name: 'TWENTY', val: 20.00}, 5 { name: 'TEN', val: 10.00}, 6 { name: 'FIVE', val: 5.00}, 7 { name: 'ONE', val: 1.00}, 8 { name: 'QUARTER', val: 0.25}, 9 { name: 'DIME', val: 0.10}, 10 { name: 'NICKEL', val: 0.05}, 11 { name: 'PENNY', val: 0.01} 12 ]; 13 function checkCashRegister(price, cash, cid) { 14 //需要找零的钱 15 var change = cash - price; 16 //把收银台的钱求和,同时转变成一个对象 17 var register = cid.reduce(function(prev, curr) { 18 prev.total += curr[1]; 19 prev[curr[0]] = curr[1]; 20 return prev; 21 }, {total: 0}); 22 //简单的判断 23 if (register.total === change) { 24 return 'Closed'; 25 } 26 if (register.total < change) { 27 return 'Insufficient Funds'; 28 } 29 30 // 循环我们之前定义的二维数组,并求出我们想要返回的面值数组 31 var change_arr = denom.reduce(function(prev, curr) { 32 var value = 0; 33 //把收银台里的面值从大到小的去筛选,并判断这张面值能否找零 34 while (register[curr.name] > 0 && change >= curr.val) { 35 change -= curr.val;//满足条件就扣除这么大的一张面值,好比总共找零60,先给一张50 36 register[curr.name] -= curr.val;//同时收银台 37 value += curr.val; 38 //熟悉的朋友知道JavaScript精度的问题,很多时候2其实是1.999999999999,所以这里处理下。 39 change = Math.round(change * 100) / 100; 40 //value的值可以不用和0判断,直接把结果存入prev中。下面的式子判断了,反而结果不准确 //prev.push([ curr.name, value ]); 41 } 42 // 如果value有值,就可以加入要返回的数组了,否则比较下一阶的面值 43 if (value > 0) { //判断可以放在上面进行,这里的判断没有考虑到最后恰好等于0 的情况,所以不对 44 prev.push([ curr.name, value ]); 45 } 46 return prev; 47 }, []); 48 //数组里没有值 ,或者需要找零的钱不够 49 if (change_arr.length < 1 || change > 0) { 50 return "Insufficient Funds"; 51 } 52 return change_arr; 53 }
4.Inventory Update
依照一个存着新进货物的二维数组,更新存着现有库存(在 arr1
中)的二维数组. 如果货物已存在则更新数量 . 如果没有对应货物则把其加入到数组中,更新最新的数量. 返回当前的库存数组,且按货物名称的字母顺序排列.
思路:就是大学里小卖部进货,巧克力面包有的,就把数量累计。新来的苹果面包没有,就直接入库。同时苹果面包是A开头,巧克力面包是C开头,所以苹果面包在返回的数组中应该排在前面。纯粹就是考察数组的知识点。代码注释会具体讲
function updateInventory(arr1, arr2) { //定义空数组,然后在把arr1整个数组copy给它,很多人会问为什么不能arr = arr1; //因为数组是引用类型,如果用=赋值,他们其实指向的是一个地址,会面的代码有可能会报错 var arr = []; arr = arr1.map(function(item){ return item; }) //思路很简单,将第二个数组的每个元素与库存作比较,有,就把数值累加,没有就直接插入 for(var i = 0;i < arr2.length;i++){ var flag = true; for(var j = 0; j < arr1.length; j++){ if (arr2[i][1] == arr1[j][1]){ arr1[j][0] += arr2[i][0]; arr.push(arr1[j]);
//arr.push(arr1[j]); //注释这个代码,就不用去重了 flag = false; } } if(flag){ arr.push(arr2[i]); } } //去重 var tempArr = arr.filter(function(item,index,array){ return array.indexOf(item) == index; }); //排序 return tempArr.sort(function(a,b){ return (a[1][0]>b[1][0])? 1 : 0; }); }
思路
分析题意,现有库存arr1
和新进货物arr2
都是二维数组,每一项又能拆分为value
、key
两种。我们要把arr2
中的key
与arr1
中的key
进行比较,如果相同,则把arr1
中相应的value
值加上新进货物进行更新,如果arr1
中的key
没有相同值,说明该项是新品种货物,需要把整项加入arr1
数组进行更新。
当现有库存arr1
为空时,新进货物将全部更新为库存:
if(arr1.length === 0){ arr1 = arr2; }
将arr1
中每一项与arr2
逐条对比:
arr1.forEach(function(item,index,array){ for(var i = 0; i < arr2.length; i++){ //do something... } });
当arr1
中已存在相同货物时:
if(item[1] == arr2[i][1]){ item[0] += arr2[i][0]; break; }
arr1
中尚未有该货物:
if(array.join().indexOf(arr2[i][1]) < 0){ array.push(arr2[i]); }
循环外部对得到的arr1
数组按key
进行升序排序:
arr1.sort(function(a,b){ return a[1] > b[1]; });
function updateInventory(arr1, arr2) { // All inventory must be accounted for or you're fired! if(arr1.length === 0){ arr1 = arr2; } else{ arr1.forEach(function(item,index,array){ for(var i = 0; i < arr2.length; i++){ //货物已存在 if(item[1] == arr2[i][1]){ item[0] += arr2[i][0]; break; } //无对应货物 if(array.join().indexOf(arr2[i][1]) < 0){ array.push(arr2[i]); } } }); } arr1.sort(function(a,b){ return a[1] > b[1]; }); return arr1; }
5.No repeats please
把一个字符串中的字符重新排列生成新的字符串,返回新生成的字符串里没有连续重复字符的字符串个数.连续重复只以单个字符为准
例如, aab
应该返回 2 因为它总共有6中排列 (aab
, aab
, aba
, aba
, baa
, baa
), 但是只有两个 (aba
and aba
)没有连续重复的字符 (在本例中是 a
).
难
function permAlone(str) { var re = /(.)\1+/g, arr = str.split(''), newArr = []; //全部相等时返回0 if (str.match(re) !== null && str.match(re)[0] === str) return 0; // 创建一个交换函数来交换变量的内容 var temp; function change(index1, index2) { temp = arr[index1]; arr[index1]=arr[index2]; arr[index2]=temp; } //创建递归函数,生成所情况的组合 function generate(n) { //递归至只身下一个的时候,可以返回这个数组了。 if(n === 1){ newArr.push(arr.join('')); } else{ //重点就是这里,整个题目所有所有的关键就这两步! for(var i=0; i){ //自身调用,挨个排好 generate(n-1); //奇数取0,偶数取i change(n % 2? 0 : i, n - 1); } } } //调用函数,然后去掉不符合要求的元素,再返回长度 generate(arr.length); var result = newArr.filter(function(string) { return !string.match(re); }); return result.length; }
6.Friendly Date Ranges
把常见的日期格式如:YYYY-MM-DD
转换成一种更易读的格式。
易读格式应该是用月份名称代替月份数字,用序数词代替数字来表示天 (1st
代替 1
).
记住不要显示那些可以被推测出来的信息: 如果一个日期区间里结束日期与开始日期相差小于一年,则结束日期就不用写年份了。月份开始和结束日期如果在同一个月,则结束日期月份就不用写了。
另外, 如果开始日期年份是当前年份,且结束日期与开始日期小于一年,则开始日期的年份也不用写。
例如: makeFriendlyDates(["2016-07-01", "2016-07-04"]) 应该返回 ["July 1st","4th"] makeFriendlyDates(["2016-07-01", "2018-07-04"]) 应该返回 ["July 1st, 2016", "July 4th, 2018"].
function makeFriendlyDates(arr) { return arr; } makeFriendlyDates(["2016-07-01", "2016-07-04"]) should return ["July 1st","4th"]. makeFriendlyDates(["2016-12-01", "2017-02-03"]) should return ["December 1st","February 3rd"]. makeFriendlyDates(["2016-12-01", "2018-02-03"]) should return ["December 1st, 2016","February 3rd, 2018"]. makeFriendlyDates(["2017-03-01", "2017-05-05"]) should return ["March 1st, 2017","May 5th"] makeFriendlyDates(["2018-01-13", "2018-01-13"]) should return ["January 13th, 2018"]. makeFriendlyDates(["2022-09-05", "2023-09-04"]) should return ["September 5th, 2022","September 4th"]. makeFriendlyDates(["2022-09-05", "2023-09-05"]) should return ["September 5th, 2022","September 5th, 2023"].
function makeFriendlyDates(arr) { //创建基本数组,注意索引从0开始,所以日期和月份数组的第一个字符为空。 var dateArr = ["","1st","2nd","3rd","4th","5th","6th","7th","8th","9th","10th", "11th","12th","13th","14th","15th","16th","17th","18th","19th","20th", "21st","22nd","23rd","24th","25th","26th","27th","28th","29th","30th", "31st"], monthArr = ["","January","February","March","April","May","June", "July","August","September","October","November","December"], result = [], temparr1 = arr[0].split("-"), temparr2 = arr[1].split("-"); //当前的四位数年份 var nowYear = new Date().getFullYear(); //创建用于判断的年丶月丶日 var year1 = temparr1[0], month1 = temparr1[1], date1 = temparr1[2], year2 = temparr2[0], month2 = temparr2[1], date2 = temparr2[2]; //年丶月丶日的差值 var Y = year2 - year1, M = month2 - month1, D = date2 - date1; //转换最后需要输出的月丶日 var remonth1 = monthArr[parseInt(month1)], redate1 = dateArr[parseInt(date1)], remonth2 = monthArr[parseInt(month2)], redate2 = dateArr[parseInt(date2)]; //开始判断区间大于1年的情况 if(Y > 1 || (Y == 1 && M >= 1) || (Y == 1 && M == 0 && D >= 0)){ var result1 = remonth1+" "+redate1+", "+year1; var result2 = remonth2+" "+redate2+", "+year2; result.push(result1); result.push(result2); } //判断小于1年的第一种情况--年份不同 else if(Y == 1 && M < 1){ if(year1 == nowYear){ var result1 = remonth1+" "+redate1; var result2 = remonth2+" "+redate2; result.push(result1); result.push(result2); } else{ var result1 = remonth1+" "+redate1+", "+year1; var result2 = remonth2+" "+redate2; result.push(result1); result.push(result2); } } //判断小于1年的第二种情况--年份相同 else{ if(M == 0){ //同月同日 if(D == 0){ if (year1 == nowYear) { var result1 = remonth1+" "+redate1; result.push(result1); } else{ var result1 = remonth1+" "+redate1+", "+year1; result.push(result1); } } //同月不同日 else{ var result1 = remonth1+" "+redate1; var result2 = redate2; result.push(result1); result.push(result2); } } //不同月 else{ var result1 = remonth1+" "+redate1+", "+year1; var result2 = remonth2+" "+redate2; result.push(result1); result.push(result2); } } return result; }
sss
7.Make a Person
用下面给定的方法构造一个对象.
方法有 getFirstName(), getLastName(), getFullName(), setFirstName(first), setLastName(last), and setFullName(firstAndLast).
所有有参数的方法只接受一个字符串参数.
所有的方法只与实体对象交互.
var Person = function(firstAndLast) { return firstAndLast; }; var bob = new Person('Bob Ross'); bob.getFullName(); bob.getFirstName() 应该返回 "Bob". bob.getLastName() 应该返回 "Ross". bob.getFullName() 应该返回 "Bob Ross". bob.getFullName() 应该返回 "Haskell Ross" after bob.setFirstName("Haskell"). bob.getFullName() 应该返回 "Haskell Curry" after bob.setLastName("Curry"). bob.getFullName() 应该返回 "Haskell Curry" 在 bob.setFullName("Haskell Curry") 之后. bob.getFirstName() 应该返回 "Haskell" 在 bob.setFullName("Haskell Curry") 之后. bob.getLastName() 应该返回 "Curry" 在 bob.setFullName("Haskell Curry") 之后.
function Person(fullName){ this.getFirstName=function(){ return fullName.split(' ')[0]; }; this.getLastName=function(){ return fullName.split(' ')[1]; }; this.getFullName=function(){ return fullName; }; this.setFirstName=function(first){ var arr=fullName.split(' '); arr.splice(0,1,first); fullName=arr.join(' '); }; this.setLastName=function(last){ var arr=fullName.split(' '); arr.splice(1,1,last); fullName=arr.join(' '); }; this.setFullName=function(firstAndLast){ fullName=firstAndLast; }; }
8.Map the Debris
返回一个数组,其内容是把原数组中对应元素的平均海拔转换成其对应的轨道周期.
原数组中会包含格式化的对象内容,像这样{name: 'name', avgAlt: avgAlt}
.
至于轨道周期怎么求,戳这里 on wikipedia (不想看英文的话可以自行搜索以轨道高度计算轨道周期的公式).
求得的值应该是一个与其最接近的整数,轨道是以地球为基准的.
地球半径是 6367.4447 kilometers, 地球的GM值是 398600.4418, 圆周率为Math.PIorbitalPeriod([{name : "sputnik", avgAlt : 35873.5553}])
应该返回 [{name: "sputnik", orbitalPeriod: 86400}]
.
orbitalPeriod([{name: "iss", avgAlt: 413.6}, {name: "hubble", avgAlt: 556.7}, {name: "moon", avgAlt: 378632.553}])
应该返回 [{name : "iss", orbitalPeriod: 5557}, {name: "hubble", orbitalPeriod: 5734}, {name: "moon", orbitalPeriod: 2377399}]
.
function orbitalPeriod(arr) { var GM = 398600.4418; var earthRadius = 6367.4447; return arr; } orbitalPeriod([{name : "sputnik", avgAlt : 35873.5553}]);
在此需要补下高一物理的课。以万有引力做向心力,则GMm/R^2=mrω^2 ω=2π/T,R=r+h,所以T=2π(r+h)·sqr((r+h)/GM)。
function orbitalPeriod(arr) { var GM = 398600.4418; var earthRadius = 6367.4447; for(var i=0;i){ var R=(arr[i].avgAlt+6367.4447); var T=R*2*Math.PI*Math.sqrt((R/GM)); delete arr[i].avgAlt; arr[i].orbitalPeriod=Math.round(T); } console.log(arr) return arr; }
9.Pairwise
举个例子:有一个能力数组[7,9,11,13,15]
,按照最佳组合值为20来计算,只有7+13和9+11两种组合。而7在数组的索引为0,13在数组的索引为3,9在数组的索引为1,11在数组的索引为2。
所以我们说函数:pairwise([7,9,11,13,15],20)
的返回值应该是0+3+1+2的和,即6。
我们可以通过表格来更直观地查看数组中索引和值的关系:
Index | 0 | 1 | 2 | 3 | 4 |
---|---|---|---|---|---|
Value | 7 | 9 | 11 | 13 | 15 |
function pairwise(arr, arg) { var sum = 0, len = arr.length; for(var i = 0; i < len; i++){ for(var j = i+1;j < len; j++){ if(arr[i]+arr[j] == arg){ sum =sum + i +j; arr[i] = "null"; arr[j] = "null"; } } } return sum; }
这题,看题面的意思,就是在数组中找两个和为传入参数arg
的项,把它们的索引值相加并返回。但事实上,我们还得考虑几种特殊情况。
例如,测试数据的第三项:
pairwise([1, 1, 1], 2)
应该返回 1.
也就是说,对于单项相同大小的数组项,只考虑由前至后遍历时遇到的第一组解。
测试数据的第五项:
pairwise([], 100)
应该返回 0.
这个好解决,只要在函数前部加一个判断就可以啦。如果传入空数组,则返回0.
- if(arr.length === 0){
- return 0;
- }
回到题目的主要逻辑,要解决这个问题,其实就是两个arr在自己和自己比较。看看这一项加另一项是否等于期望值。为了让两个arr自我遍历,我用了两个for()
循环进行嵌套。
- for(var i = 0; i < arr.length; i++){
- for(var j = 0; j < arr.length; j++){
- //do something...
- }
- }
内层for()
循环的内部,就是我们的主战场了。当 i 和 j 相等时,它们指向的是同一项,这种情况是要排除的。同时,加上题目的要求,寻找两项和为arg
的项。
- if(i !== j && arr[i] + arr[j] === arg){
- //do something...
- }
我们需要在循环外部声明一个空数组来接收符合条件的索引值,恩,假设我们已经有了一个名为temp
的空数组。
- temp.push(i,j);
为了不使序号已经在结果集中的对应项重复被遍历,我们可以使用delete
删除对应下标的项。这种方式数组长度不变,被删除的项在数组中再次访问就变为undefined了,原来数组的索引也保持不变。
- delete arr[i];
- delete arr[j];
这样等同于:
- arr[i] = undefined;
- arr[j] = undefined;
最后,把得到的结果集用reduce()
进行归并,完美!
function pairwise(arr, arg) { var temp = []; if(arr.length === 0){ return 0; } for(var i = 0; i < arr.length; i++){ for(var j = 0; j < arr.length; j++){ //j的条件可以换下,这样写太麻烦 if(i !== j && arr[i] + arr[j] === arg){ temp.push(i,j); delete arr[i]; delete arr[j]; break; } } } return temp.reduce(function(prev,cur){ return prev + cur; }); }