JavaScript专项算法题1回调和高阶函数

2次阅读

共计 13257 个字符,预计需要花费 34 分钟才能阅读完成。

@冒泡的马树

题库原地址:http://csbin.io/callbacks

挑战 1

问题:

构建一个 addTwo 函数,作用为接受一个参数并将参数加 2。

题解:

// Challenge 1
function addTwo(num) {return num + 2;}

// To check if you've completed it, uncomment these console.logs!
console.log(addTwo(3));
console.log(addTwo(10));

挑战 2

问题:

构建一个 addS 函数,作用为接受一个参数并将参数与“S” 拼接。

题解:

// Challenge 2
function addS(word) {return word + "S";}

// uncomment these to check your work
console.log(addS('pizza'));
console.log(addS('bagel'));

挑战 3

问题:

构建一个 map 函数,其接受两个参数:

  1. 数值型数组
  2. 回调函数——一个应用于上述数值型数组中的每一个元素的函数(于 map 函数内)

map 函数的返回值为包含上述数值型数组元素逐个运行回调函数后生成的元素值的新数组。

map([1,2,3,4,5], multiplyByTwo); //-> [2,4,6,8,10]
multiplyByTwo(1); //-> 2
multiplyByTwo(2); //-> 4

题解:

// Challenge 3
function map(array, callback) {var newArray = [];
  for (let i = 0; i < array.length; i++) {newArray.push(callback(array[i]));
  }
  return newArray;
}

console.log(map([1, 2, 3], addTwo));

挑战4

问题:

函数 forEach 接受一个数组和一个回调函数,运行回调函数于输入数组的每一个元素。forEach 函数无返回值。

let alphabet = '';
const letters = ['a', 'b', 'c', 'd'];
forEach(letters, function(char) {alphabet += char;});
console.log(alphabet);   //prints 'abcd'

题解:

// Challenge 4
function forEach(array, callback) {for (let i =0; i < array.length; i++) {callback(array[i]);
  }
}

// see for yourself if your forEach works!
let alphabet = '';
const letters = ['a', 'b', 'c', 'd'];
forEach(letters, function(char) {alphabet += char;});
console.log(alphabet);   //prints 'abcd'

挑战 5

问题:

在这个挑战中,你需要将 map 函数重构为 mapWith。这一次你要在 mapWith 中使用 forEach 函数而不是使用 for 循环。

题解:

// Challenge 5
function mapWith(array, callback) {var newArray = [];
  forEach(array, function(item){newArray.push(callback(item))});
  return newArray;
}

console.log(mapWith([1, 2, 3], addTwo));

挑战 6

问题:

函数 reduce 接受一个数组并将数组内的所有值合并为一个值。比如,它可以将数组求和,求积,以及其它你想加进函数中的操作。

const nums = [4, 1, 3];
const add = function(a, b) {return a + b;}
reduce(nums, add, 0);   //-> 8

以下是它的运行原理。函数有一个“累加器值”(第三个参数),作用为充当初始值并且累加每一次循环的输出值。数组参数会被遍历,传递“累加器值“和新的数组元素值作为参数到回调函数中。回调函数的返回值会成为新的”累加器值“。下一个循环会使用这个新”累加器值“。在上面的例子中,”累加器值“刚开始为 0,调用 add(0, 4),”累加器值“变为 4,然后 add(4, 1) 将其变为 5,最后 add(5, 3) 得到 8 并最终返回。

题解:

// Challenge 6
function reduce(array, callback, initialValue) {
  // // Solution 1:// var callbackValue = 0;
  // var finalResult = 0;
  // for(let i = 0; i<array.length; i++) {//  if (i == 0){// callbackValue = array[i];
  // finalResult = callback(callbackValue, initialValue);
  // } else {// finalResult = callback(finalResult, array[i]);
  // }
  // }
  // return finalResult;
  
  // Solution 2:
  let reduceValue = initialValue;
  for(let i = 0; i < array.length; i++){reduceValue = callback(array[i], reduceValue);
  }
  return reduceValue;
}

const nums = [4, 1, 3];
const add = function(a, b) {return a + b;}
console.log(reduce(nums, add, 0)); //-> 8

挑战 7

问题:

构建 intersection 函数,作用为比较输入进来的多组数组并返回一个包含数组间共同元素的新数组。奖励:使用 reduce!

题解:

// Challenge 7
function intersection(arrays) {// Solution 1 (without reduce): 
  // var obj = {};
  // for(let i=0; i<arrays.length; i++){//   if(i==0){//     for(var j=0;j<arrays[i].length; j++){//       obj[arrays[i][j]]=arrays[i][j];
  //     }
  //   } else {// for(var j=0;j<arrays[i].length; j++){//     // console.log(obj[arrays[i][j]])
  //     if(obj[arrays[i][j]] != undefined){//       obj[arrays[i][j]]= obj[arrays[i][j]] + 1;
  //     }
  //   }
  // }
  // }
  // for(var k in obj) {//   if(k != obj[k] - arrays.length + 1) {//     delete obj[k];
  //   }
  // }
  // var intersectionArray = [];
  // for(var z in obj){//   intersectionArray.push(parseInt(z));
  // }
  // return intersectionArray;
    
  // Solution 2 (with reduce):
  return arrays.reduce((acc, curr) => {return curr.filter(el => acc.includes(el));
  })
}
  

console.log(intersection([[5, 10, 15, 20], [15, 88, 1, 5, 7], [1, 10, 15, 5, 20]]));
// should log: [5, 15]

挑战8

问题:

构建 union 函数,作用为比较输入进来的多组数组并返回一个包含输入数组中所有元素的新数组。如果存在重复元素,则新数组中仅保留一个,另外需保留新数组的元素顺序为从第一个输入数组的第一个元素开始。奖励:使用 reduce!

题解:

// Challenge 8
function union(arrays) {// // Solution 1 (without reduce):
  // var obj = {};
  // for(let i=0; i<arrays.length; i++){//   if(i==0){//     for(var j=0;j<arrays[i].length; j++){//       obj[arrays[i][j]]=arrays[i][j];
  //     }
  //   } else {// for(var j=0;j<arrays[i].length; j++){//     // console.log(obj[arrays[i][j]])
  //     if(obj[arrays[i][j]] == undefined){//       obj[arrays[i][j]]= arrays[i][j];
  //     }
  //   }
  // }
  // }
  // var unionArray = [];
  // for(var z in obj){//   unionArray.push(parseInt(z));
  // }
  // return unionArray;

  // Solution 2 (with reduce): 
  return arrays.reduce((acc, curr) => {const newElements = curr.filter(el => !acc.includes(el));
    return acc.concat(newElements);
  }
  )
}

console.log(union([[5, 10, 15], [15, 88, 1, 5, 7], [100, 15, 10, 1, 5]]));
// should log: [5, 10, 15, 88, 1, 7, 100]

挑战 9

问题:

构建 objOfMatches 函数,接受两个数组和一个回调函数作为参数,作用为创建一个特定对象并返回。objOfMatches 会使用回调函数测试第一个数组的每一个元素以确认其输出是否匹配于第二个数组内相同下标的元素。如果匹配,第一个数组内的这个元素会成为所创建对象的键,而第二个数组内的相同下标元素则会成为对应的值。

题解:

// Challenge 9
function objOfMatches(array1, array2, callback) {const matchObj = {};
  for (let i = 0; i < array1.length; i++){if(callback(array1[i]) === array2[i]) {matchObj[array1[i]] = array2[i];
    }
  }
  return matchObj;
}

console.log(objOfMatches(['hi', 'howdy', 'bye', 'later', 'hello'], ['HI', 'Howdy', 'BYE', 'LATER', 'hello'], function(str) {return str.toUpperCase(); }));
// should log: {hi: 'HI', bye: 'BYE', later: 'LATER'}

挑战 10

问题:

构建 multiMap 函数,接受两个数组作为参数,第一个数组的元素为值而第二个数组的元素为回调函数。multiMap 会返回一个特定对象,该对象的键为第一个数组的值,键对应的值则是将键依序传入第二个数组的回调函数中得到的返回值组成的新数组。

题解:

// Challenge 10
function multiMap(arrVals, arrCallbacks) {const multiMapObj = {};
  let tempArray = [];
  arrVals.forEach(el => {tempArray = [];
    for(let i =0; i<arrCallbacks.length; i++){tempArray.push(arrCallbacks[i](el));
    }
    multiMapObj[el] = tempArray;
  })
  return multiMapObj;
}

console.log(multiMap(['catfood', 'glue', 'beer'], [function(str) {return str.toUpperCase(); }, function(str) {return str[0].toUpperCase() + str.slice(1).toLowerCase();}, function(str) {return str + str;}]));
// should log: {catfood: ['CATFOOD', 'Catfood', 'catfoodcatfood'], glue: ['GLUE', 'Glue', 'glueglue'], beer: ['BEER', 'Beer', 'beerbeer'] }

挑战 11

问题:

构建 objectFilter 函数,第一个参数为对象,第二个参数为回调函数。objectFilter 函数会返回特定对象。此特定对象所包含的属性为输入对象中值与其对应键传入回调函数得到的返回值一致的键值对。

题解:

// Challenge 11
function objectFilter(obj, callback) {const objectFilterObj = {};
  for(let key in obj) {if(obj[key] === callback(key)){objectFilterObj[key] = obj[key];
    }
  }
  return objectFilterObj;
}

const cities = {
London: 'LONDON',
LA: 'Los Angeles',
Paris: 'PARIS',
};
console.log(objectFilter(cities, city => city.toUpperCase())) // Should log {London: 'LONDON', Paris: 'PARIS'}

挑战 12

问题:

构建 majority 函数,接受参数为一个数组和一个回调函数。回调函数的返回值为 true 或 false。majority 会遍历输入数组的元素并且对其运行回调函数,直到能够确定大多数回调函数的返回值为 true。如果返回 true 的数目与返回 false 的数目相同,majority 应返回 false。

题解:

// Challenge 12
function majority(array, callback) {
  let trueCounter = 0;
  let falseCounter = 0;
  array.forEach(el => {if(callback(el)) {trueCounter++;} else{falseCounter++;}
  });
  return (trueCounter>falseCounter)?true:false;
  
}

/*** Uncomment these to check your work! ***/
// const isOdd = function(num) {return num % 2 === 1;};
console.log(majority([1, 2, 3, 4, 5], isOdd)); // should log: true
console.log(majority([2, 3, 4, 5], isOdd)); // should log: false

挑战 13

问题:

构建 prioritize 函数,接受参数为一个数组和一个回调函数。回调函数的返回值为 true 或 false。prioritize 会遍历输入数组的元素并且对其运行回调函数,然后返回一个新数组。这个新数组会先储存输入数组中被回调函数返回 true 的元素,再储存输入数组中剩下的元素。

题解:

// Challenge 13
function prioritize(array, callback) {const prioritizeArray = [];
  const restArray = [];
  array.forEach(el => {if(callback(el)){prioritizeArray.push(el);
    } else {restArray.push(el);
    }
  })
  return prioritizeArray.concat(restArray); 
}

/*** Uncomment these to check your work! ***/
const startsWithS = function(str) {return str[0] === 's' || str[0] === 'S'; };
console.log(prioritize(['curb', 'rickandmorty', 'seinfeld', 'sunny', 'friends'], startsWithS)); // should log: ['seinfeld', 'sunny', 'curb', 'rickandmorty', 'friends']

挑战 14

问题:

构建 countBy 函数,接受参数为一个数组和一个回调函数,返回值为一个特定对象。countBy 会遍历输入数组的元素并对其运行回调函数。每一个运行回调函数得到的返回值会被储存为特定对象的键,而键对应的值则为得到此返回值的回调函数运行的次数。

题解:

// Challenge 14
function countBy(array, callback) {const countByObj = {};
  array.forEach(el => {if(countByObj[callback(el)] == undefined) {countByObj[callback(el)] = 1;
    } else {countByObj[callback(el)]++;
    }
  })
  return countByObj;
}

/*** Uncomment these to check your work! ***/
console.log(countBy([1, 2, 3, 4, 5], function(num) {if (num % 2 === 0) return 'even';
else return 'odd';
})); // should log: {odd: 3, even: 2}

挑战 15

问题:

构建 groupBy 函数,接受参数为一个数组和一个回调函数,返回值为一个特定对象。groupBy 会遍历输入数组的元素并对其运行回调函数。每一个运行回调函数得到的返回值会被储存为特定对象的键,而键对应的值则为一个由导致回调函数产生此返回值的输入数组的元素组成的数组。

题解:

// Challenge 15
function groupBy(array, callback) {const groupByObj = {};
  array.forEach(el => {if(groupByObj[callback(el)] === undefined) {groupByObj[callback(el)] = [el];
    } else {groupByObj[callback(el)] = groupByObj[callback(el)].concat([el]); 
    }
  })
  return groupByObj;
}

/*** Uncomment these to check your work! ***/
const decimals = [1.3, 2.1, 2.4];
const floored = function(num) {return Math.floor(num); };
console.log(groupBy(decimals, floored)); // should log: {1: [1.3], 2: [2.1, 2.4] }

挑战 16

问题:

构建 goodKeys 函数,接受参数为一个对象和一个回调函数。回调函数的返回值为 true 或 false。goodKeys 会遍历输入对象并运行回调函数于对象的值上。goodKeys 的返回值为一个由运行回调函数后返回 true 的对象值所对应的对象键组成的数组。

题解:

// Challenge 16
function goodKeys(obj, callback) {const goodKeysArray = [];
  for(let key in obj) {if(callback(obj[key])) {goodKeysArray.push(key);
    }
  }
  return goodKeysArray;
}

/*** Uncomment these to check your work! ***/
const sunny = {mac: 'priest', dennis: 'calculating', charlie: 'birdlaw', dee: 'bird', frank: 'warthog'};
const startsWithBird = function(str) {return str.slice(0, 4).toLowerCase() === 'bird';};
console.log(goodKeys(sunny, startsWithBird)); // should log: ['charlie', 'dee']

挑战 17

问题:

构建 commutative 函数,接受参数为两个回调函数和一个值。commutative 会返回一个布尔值,从而表明运行第一个回调函数于输入值,再将得到的返回值输入到第二个回调函数中运行,得到的结果与逆序操作是否相同(即运行输入值于第二个回调函数,得到的返回值再输入到第一个回调函数中)。

题解:

// Challenge 17
function commutative(func1, func2, value) {if(func1(func2(value)) === func2(func1(value))) {return true;} else {return false;}
}

/*** Uncomment these to check your work! ***/
const multBy3 = n => n * 3;
const divBy4 = n => n / 4;
const subtract5 = n => n - 5;
console.log(commutative(multBy3, divBy4, 11)); // should log: true
console.log(commutative(multBy3, subtract5, 10)); // should log: false
console.log(commutative(divBy4, subtract5, 48)); // should log: false

挑战 18

问题:

构建 objFilter 函数,接受参数为一个对象和一个回调函数,返回值为一个特定对象。objFilter 会遍历输入对象,使用输入对象的键作为回调函数的输入。如果回调函数的输出与对应的对象值相等,此键值对会被复制到特定对象中。最后 objFilter 返回此特定对象。

题解:

// Challenge 18
function objFilter(obj, callback) {const objectFilterObj = {};
  for(let key in obj) {if(obj[key] === callback(key)){objectFilterObj[key] = obj[key];
    }
  }
  return objectFilterObj;
}

/*** Uncomment these to check your work! ***/
const startingObj = {};
startingObj[6] = 3;
startingObj[2] = 1;
startingObj[12] = 4;
const half = n => n / 2;
console.log(objFilter(startingObj, half)); // should log: {2: 1, 6: 3}

挑战 19

问题:

构建 rating 函数,接受参数为一个由函数组成的数组和一个值。数组中的函数的返回值皆为 true 或 false。rating 会返回一个表明将输入值运行于数组中的函数会返回 true 的百分比数。

题解:

// Challenge 19
function rating(arrOfFuncs, value) {
  let trueCounter = 0;
  arrOfFuncs.forEach(el=> {if(el(value)){trueCounter++;}
  })
  return trueCounter/arrOfFuncs.length * 100;
}

/*** Uncomment these to check your work! ***/
const isEven = n => n % 2 === 0;
const greaterThanFour = n => n > 4;
const isSquare = n => Math.sqrt(n) % 1 === 0;
const hasSix = n => n.toString().includes('6');
const checks = [isEven, greaterThanFour, isSquare, hasSix];
console.log(rating(checks, 64)); // should log: 100
console.log(rating(checks, 66)); // should log: 75

挑战 20

问题:

构建 pipe 函数,接受参数为一个由函数组成的数组和一个值。pipe 会将输入值输入到数组的第一个函数中,然后再将得到的输出值输入到第二个函数中,然后输出值又再输入到第三个函数中,一直下去,直到得到数组的最后一个函数的输出值。pipe 会返回这个最终输出值。

题解:

// Challenge 20
function pipe(arrOfFuncs, value) {
  let output = value;
  arrOfFuncs.forEach(el => {output = el(output);
  })
  return output;
}

/*** Uncomment these to check your work! ***/
const capitalize = str => str.toUpperCase();
const addLowerCase = str => str + str.toLowerCase();
const repeat = str => str + str;
const capAddlowRepeat = [capitalize, addLowerCase, repeat];
console.log(pipe(capAddlowRepeat, 'cat')); // should log: 'CATcatCATcat'

挑战 21

问题:

构建 highestFunc 函数,接受参数为一个对象(包含函数)和一个值。highestFunc 会返回输入对象中运行输入值后得到最高值的函数所对应的键。

题解:

// Challenge 21
function highestFunc(objOfFuncs, subject) {
  let maxKey = "";
  let maxNumber = Number.NEGATIVE_INFINITY;
  for(let key in objOfFuncs) {if(objOfFuncs[key](subject) > maxNumber) {maxNumber = objOfFuncs[key](subject);
      maxKey = key;
    }
  }
  return maxKey;
}

/*** Uncomment these to check your work! ***/
const groupOfFuncs = {};
groupOfFuncs.double = n => n * 2;
groupOfFuncs.addTen = n => n + 10;
groupOfFuncs.inverse = n => n * -1;
console.log(highestFunc(groupOfFuncs, 5)); // should log: 'addTen'
console.log(highestFunc(groupOfFuncs, 11)); // should log: 'double'
console.log(highestFunc(groupOfFuncs, -20)); // should log: 'inverse'

挑战 22

问题:

构建 combineOperations 函数,接受参数为一个初始值和一个由函数组成的数组。conbineOperations 会将初始值输入到输入数组的第一个函数中,得到的输出值再输入到第二个函数中,一直下去,直到输入数组中的每一个函数都被调用。combineOperations 会返回输入数组的最后一个函数的输出值。

题解:


// Challenge 22
function combineOperations(startVal, arrOfFuncs) {
  let output = startVal;
  arrOfFuncs.forEach(el => {output = el(output);
  })
  return output;
}

function add100(num) {return num + 100;}

function divByFive(num) {return num / 5;}

function multiplyByThree(num) {return num * 3;}

function multiplyFive(num) {return num * 5;}

function addTen(num) {return num + 10;}

/*** Uncomment these to check your work! ***/
console.log(combineOperations(0, [add100, divByFive, multiplyByThree])); // Should output 60 -->
console.log(combineOperations(0, [divByFive, multiplyFive, addTen])); // Should output 10

挑战 23

问题:

构建 muFunc 函数,接受参数为一个数组和一个回调函数。myFunc 会依序将输入数组的元素输入到回调函数中。如果回调函数返回值为 true,myFunc 会返回当前数组元素的下标。如果回调函数从不返回 true,myFunc 会返回 -1。

题解:

// Challenge 23
function myFunc(array, callback) {
  let returnIndicator = false;
  for(let i = 0; i< array.length; i++){if(callback(array[i])){
      returnIndicator = true;
      return i;
    }
  }
  if(!returnIndicator){return -1;}
}

const numbers = [2, 3, 6, 64, 10, 8, 12];
const evens = [2, 4, 6, 8, 10, 12, 64];

function isOdd(num) {return (num % 2 !== 0);
}

/*** Uncomment these to check your work! ***/
console.log(myFunc(numbers, isOdd)); // Output should be 1
console.log(myFunc(evens, isOdd)); // Output should be -1

挑战 24

问题:

编写 myForEach 函数,接受参数为一个数组和一个回调函数。myForEach 应该依序输入数组的每一个元素到回调函数中。myForEach 的作用应该尽可能与原生的 JavaScript 数组方法.forEach() 类似。

题解:


// Challenge 24
function myForEach(array, callback) {for (let i =0; i < array.length; i++) {
        // To solve edge case: 
        // var arr=[0, undefined, 2]; arr[10] = 10;
        // From https://gist.github.com/alexhawkins/28aaf610a3e76d8b8264#gistcomment-2209454
        if(i in array){callback(array[i]);
        }
  }
}

// You could not use arrow function to assign prototype method! 
Array.prototype.myForEach = function(callback)  {for(let i =0; i<this.length; i++){if(i in this) {callback(this[i], i, this);
    }
  }
};

let sum = 0;

function addToSum(num) {sum += num;}

/*** Uncomment these to check your work! ***/
const numsArray = [1, 2, 3];
myForEach(numsArray, addToSum);
console.log(sum); // Should output 6

sum = 0;
numsArray.myForEach(addToSum);
console.log(sum); // Should output 6, too. 

正文完
 0