freecodecamp advance algorithm scripting

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都是二维数组,每一项又能拆分为valuekey两种。我们要把arr2中的keyarr1中的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中排列 (aabaababaababaabaa), 但是只有两个 (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<n;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;
    }
复制代码


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.PI
orbitalPeriod([{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<arr.length;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.

  1. if(arr.length === 0){
  2. return 0;
  3. }

回到题目的主要逻辑,要解决这个问题,其实就是两个arr在自己和自己比较。看看这一项加另一项是否等于期望值。为了让两个arr自我遍历,我用了两个for()循环进行嵌套。

  1. for(var i = 0; i < arr.length; i++){
  2. for(var j = 0; j < arr.length; j++){
  3. //do something...
  4. }
  5. }

内层for()循环的内部,就是我们的主战场了。当 i 和 j 相等时,它们指向的是同一项,这种情况是要排除的。同时,加上题目的要求,寻找两项和为arg的项。

  1. if(!== j && arr[i] + arr[j] === arg){
  2. //do something...
  3. }

我们需要在循环外部声明一个空数组来接收符合条件的索引值,恩,假设我们已经有了一个名为temp的空数组。

  1. temp.push(i,j);

为了不使序号已经在结果集中的对应项重复被遍历,我们可以使用delete删除对应下标的项。这种方式数组长度不变,被删除的项在数组中再次访问就变为undefined了,原来数组的索引也保持不变。

  1. delete arr[i];
  2. delete arr[j];

这样等同于:

  1. arr[i] = undefined;
  2. 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;
  });
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值