JavaScript练习任务1

重复输入,直到输入的是一个数字

创建一个函数 readNumber,它提示输入一个数字,直到访问者输入一个有效的数字为止。
结果值必须以数字形式返回。
访问者也可以通过输入空行或点击“取消”来停止该过程。在这种情况下,函数应该返回 null。

知识点

  • isNaN(value) 将其参数转换为数字,然后检测它是否为 NaN
  • isFinite(value) 将其参数转换为数字,如果它是常规数字,则返回 true,而不是 NaN/Infinity/-Infinity

实现:

function readNumber() {
  let num;

  do {
    num = prompt("Enter a number please?", 0);
    
    console.log(num, '--', typeof num, '----', isFinite(num))
    
  } while ( !isFinite(num) );

  if (num === null || num === '') return null;

  return +num;
}

alert(`Read: ${readNumber()}`);

从 min 到 max 的随机数

内建函数 Math.random() 会创建一个在 01 之间(不包括 1)的随机数。
编写一个 random(min, max) 函数,用以生成一个在 minmax 之间的随机浮点数(不包括 max))。

// 我们需要将区间 0…1 中的所有值“映射”为范围在 min 到 max 中的值。
// 1.如果我们将 0…1 的随机数乘以 max-min,则随机数的范围将从 0…1 增加到 0..max-min。
// 2.现在,如果我们将随机数与 min 相加,则随机数的范围将为 min 到 max。
function random(min, max) {
  return min + Math.random() * (max - min);
}
alert( random(1, 5) );
alert( random(1, 5) );
alert( random(1, 5) );

从 min 到 max 的随机整数

创建一个函数 randomInteger(min, max),该函数会生成一个范围在 minmax 中的随机整数,包括 minmax

min..max 范围中的所有数字的出现概率必须相同。

方法一:

// 调整取值范围的边界
function randomInteger(min, max) {
  // 现在范围是从  (min-0.5) 到 (max+0.5)
  let rand = min - 0.5 + Math.random() * (max - min + 1);
  return Math.round(rand);
}

alert( randomInteger(1, 3) );

方法二:使用 Math.floor 来取范围从 minmax+1 的随机数

function randomInteger(min, max) {
  // here rand is from min to (max+1)
  let rand = min + Math.random() * (max + 1 - min);
  return Math.floor(rand);
}

alert( randomInteger(1, 3) );

字符串首字母大写

写一个函数 ucFirst(str),并返回首字母大写的字符串 str

function ucFirst(str) {
  if (!str) return str;

  return str[0].toUpperCase() + str.slice(1);
}

alert( ucFirst("john") ); // John

检查 spam

写一个函数 checkSpam(str),如果 str 包含 viagra 或 XXX 就返回 true,否则返回 false。
函数必须不区分大小写。

function checkSpam(str) {
  let lowerStr = str.toLowerCase();

  return lowerStr.includes('viagra') || lowerStr.includes('xxx');
}

alert( checkSpam('buy ViAgRA now') );
alert( checkSpam('free xxxxx') );
alert( checkSpam("innocent rabbit") );

截断文本

创建函数 truncate(str, maxlength) 来检查 str 的长度,如果超过 maxlength —— 应使用 “…” 来代替 str 的结尾部分,长度仍然等于 maxlength。

function truncate(str, maxlength) {
  return (str.length > maxlength) ?
    str.slice(0, maxlength - 1) + '…' : str;
}

提取货币

我们有以 “$120” 这样的格式表示的花销。意味着:先是美元符号,然后才是数值。
创建函数 extractCurrencyValue(str) 从字符串中提取数值并返回。

function extractCurrencyValue(str) {
  return +str.slice(1);
}

数组操作

let styles = ["Jazz", "Blues"];

// 从数组末端添加"Rock-n-Roll"
styles.push("Rock-n-Roll");  // ["Jazz", "Blues", "Rock-n-Roll"]

// 用 "Classics" 替换掉数组最中间的元素。查找数组最中间的元素的代码应该适用于任何奇数长度的数组。
styles[Math.floor((styles.length - 1) / 2)] = "Classics"; // ["Jazz", "Classics", "Rock-n-Roll"]

// 移除数组第一个值
let shiftItem = styles.shift() // style = ["Classics", "Rock-n-Roll"]
console.log(shiftItem) // "Jazz"

//在数组前面添加 Rap 和 Reggae
styles.unshift("Rap", "Reggae"); // style = ["Rap", "Reggae", "Classics", "Rock-n-Roll"]

在数组上下文调用

let arr = ["a", "b"];

arr.push(function() {
  alert( this ); // this指数组arr
});

arr[2](); // 执行方法,打印arr:"a,b,function(){alert(this)}"

输入数字求和(数组)

写出函数 sumInput(),要求如下:

  • 使用 prompt 向用户索要值,并存在数组中。
  • 当用户输入了非数字、空字符串或者点击“取消”按钮的时候,问询结束。
  • 计算并返回数组所有项之和。
    P.S. 0 是有效的数字,不要因为是 0 就停止问询。
function sumInput() {

  let numbers = [];

  while (true) {

    let value = prompt("请输入数字?", 0)
    
    if (value === "" || value === null || !isFinite(value)) break;

    numbers.push(+value);
  }

  let sum = 0;
  for (let item of numbers) {
    sum += item;
  }
  return sum;
}

alert( sumInput() );

将 border-left-width 转换成 borderLeftWidth(数组)

function camelize(str) {
  return str
    .split('-') // splits 'border-left-width' into array ['border', 'left', 'width']
    .map(
      // converts ['border', 'left', 'width'] into ['border', 'Left', 'Width']
      (word, index) => index == 0 ? word : word[0].toUpperCase() + word.slice(1)
    )
    .join(''); // joins ['my', 'Long', 'Word'] into 'myLongWord'
}

console.log(camelize("border-left-width")) // borderLeftWidth

过滤范围(数组)

写一个函数 filterRange(arr, a, b),该函数获取一个数组 arr,在其中查找数值大于或等于 a,且小于或等于 b 的元素,并将结果以数组的形式返回。

该函数不应该修改原数组。它应该返回新的数组。

function filterRange(arr, a, b) {
  // 在表达式周围添加了括号,以提高可读性
  return arr.filter(item => (a <= item && item <= b));
}

let arr = [5, 3, 8, 1];

let filtered = filterRange(arr, 1, 4);

console.log( filtered ); // 3,1(匹配的值)
console.log( arr ); // 5,3,8,1(未经改动的数组中的值)

原位(in place)过滤范围(数组)

写一个函数 filterRangeInPlace(arr, a, b),该函数获取一个数组 arr,并删除其中介于 a 和 b 区间以外的所有值。检查:a ≤ arr[i] ≤ b
该函数应该只修改数组。它不应该返回任何东西。

function filterRangeInPlace(arr, a, b) {

  for (let i = 0; i < arr.length; i++) {
    let val = arr[i];

    // 如果超出范围,则删除
    if (val < a || val > b) {
      arr.splice(i, 1);
      i--;
    }
  }

}

let arr = [5, 3, 8, 1];

filterRangeInPlace(arr, 1, 4); // 删除 1 到 4 范围之外的值

console.log( arr ); // [3, 1]

降序/升序排序(数组)

let arr = [5, 2, 1, -10, 8];

// 升序
arr.sort((a, b) => a - b);
console.log(arr)

// 降序
arr.sort((a, b) => b - a);
console.log( arr );

复制和排序数组

我们有一个字符串数组 arr。我们希望有一个排序过的副本,但保持 arr 不变。
创建一个函数 copySorted(arr) 返回这样一个副本。
使用 str.localeCompare 方法正确地对字母进行排序。

function copySorted(arr) {
  // slice()返回一个新的数组,再对新数组进行排序
  return arr.slice().sort((a, b) => a.localeCompare(b));
}

let arr = ["HTML", "JavaScript", "CSS"];

let sorted = copySorted(arr);

console.log( sorted );
console.log( arr );

按字段排序一组对象

let users = [
  { name: "John", age: 20, surname: "Johnson" },
  { name: "Pete", age: 18, surname: "Peterson" },
  { name: "Ann", age: 19, surname: "Hathaway" }
]
// 常规写法
// 通过 name (Ann, John, Pete)
users.sort((a, b) => a.name > b.name ? 1 : -1);

// 通过 age (Pete, Ann, John)
users.sort((a, b) => a.age > b.age ? 1 : -1);

编写函数 byField(fieldName),每次传入字段名,就可以实现排序。

function byField(fieldName){
  return (a, b) => a[fieldName] > b[fieldName] ? 1 : -1;
  // 上面代码是简写,具体实现如下
  // function compareFn (a, b){
  //	return a[fieldName] > b[fieldName] ? 1 : -1
  //}
  // return compareFn;
}

// 使用
users.sort(byField('name'));
users.sort(byField('age'));
  • byField 函数接受一个参数 fieldName 。
  • 在函数内部定义了一个名为 compareFn 的函数,它接受两个参数 ab 。通过比较 ab 对象中指定的 fieldName 属性的值来决定返回 1 (表示 a 排在 b 之后)还是 -1 (表示 a 排在 b 之前)。
  • 最后,byField 函数返回的是 compareFn 这个函数本身,而不是调用 compareFn 的结果。

知识点array.sort()
sort(compareFn),参数compareFn是定义排序顺序的函数,该函数有ab两个参数。返回值应该是一个数字,其符号表示两个元素的相对顺序:如果 a 小于 b,返回值为负数,如果 a 大于 b,返回值为正数,如果两个元素相等,返回值为 0。NaN 被视为 0。

a,第一个用于比较的元素。不会是 undefined。
b,第二个用于比较的元素。不会是 undefined。

创建一个可扩展的 calculator

创建一个构造函数 Calculator,以创建“可扩展”的 calculator 对象。
该任务由两部分组成:

  1. 首先,实现 calculate(str) 方法,该方法接受像 “1 + 2” 这样格式为“数字 运算符 数字”(以空格分隔)的字符串,并返回结果。该方法需要能够理解加号 + 和减号 -。
  2. 然后添加方法 addMethod(name, func),该方法教 calculator 进行新操作。它需要运算符 name 和实现它的双参数函数 func(a,b)
function Calculator() {
  // 存储支持的方法
  this.methods = {
    "-": (a, b) => a - b,
    "+": (a, b) => a + b
  };
  
  // 计算
  this.calculate = function(str) {

    let split = str.split(' '),
      a = +split[0],
      op = split[1],
      b = +split[2];

    if (!this.methods[op] || isNaN(a) || isNaN(b)) {
      return NaN;
    }

    return this.methods[op](a, b);
  };
  
  // 添加计算方法
  this.addMethod = function(name, func) {
    this.methods[name] = func;
  };
}


let calc = new Calculator();
console.log( calc.calculate("3 + 7") );  // 10
console.log( calc.calculate("7 - 2") );  // 5

// 添加乘法、除法、求幂运算
calc.addMethod("*", (a, b) => a * b);
calc.addMethod("/", (a, b) => a / b);
calc.addMethod("**", (a, b) => a ** b);

console.log( calc.calculate("2 ** 3") ); // 8

数组映射到数组

你有一个 user 对象数组,每个对象都有 user.name。编写将其转换为 names 数组的代码。

let john = { name: "John", age: 25 };
let pete = { name: "Pete", age: 30 };
let mary = { name: "Mary", age: 28 };

let users = [ john, pete, mary ];

let names = users.map(item => item.name)

console.log( names ); // John, Pete, Mary

数组映射到对象

你有一个 user 对象数组,每个对象都有 namesurnameid
编写代码以该数组为基础,创建另一个具有 idfullName 的对象数组,其中 fullNamenamesurname 生成。

let john = { name: "John", surname: "Smith", id: 1 };
let pete = { name: "Pete", surname: "Hunt", id: 2 };
let mary = { name: "Mary", surname: "Key", id: 3 };

let users = [ john, pete, mary ];
let usersMapped = users.map(item => {
	return {
		id: item.id,
		fullname: `${item.name} ${item.subname}`
	}
})
console.log(usersMapped)

按年龄对用户排序

编写函数 sortByAge(users) 获得对象数组的 age 属性,并根据 age 对这些对象数组进行排序。

let john = { name: "John", age: 25 };
let pete = { name: "Pete", age: 30 };
let mary = { name: "Mary", age: 28 };

let arr1 = [ pete, john, mary ];

let sortByAge = (arr) => {
	arr.sort((a, b) => a.age - b.age);
}
sortByAge(arr1);

获取平均数(数组)

function getAverage(arr) {
  let total = arr.reduce((prev, current) => prev + current, 0)
  return total / arr.length;
}

let arr1 = [5, 17, 10, 28, 59]

console.log( getAverage(arr1) );

数组去重

利用for...of遍历去重

function unique(arr) {
  let result = [];

  for (let str of arr) {
    if (!result.includes(str)) {
      result.push(str);
    }
  }

  return result;
}

let arr= ["Hare", "Krishna", "Hare", "Krishna",
  "Krishna", "Krishna", "Hare", "Hare", ":-O"
];

console.log( unique(arr) ); // ['Hare', 'Krishna', ':-O']

使用 Set 来存储唯一值

let arr= ["Hare", "Krishna", "Hare", "Krishna",
  "Krishna", "Krishna", "Hare", "Hare", ":-O"
];
let set = new Set(arr)
let uniqueArr = Array.from(set)
console.log(uniqueArr) // ['Hare', 'Krishna', ':-O']

// 简洁写法
function unique(arr) {
  return Array.from(new Set(arr));
}
let uniqueArr = unique(arr)

从数组创建键(值)对象

假设我们收到了一个用户数组,形式为:{id:..., name:..., age:... }

创建一个函数 groupById(arr) 从该数组创建对象,以 id 为键(key),数组项为值。

let users = [
  {id: 'john', name: "John Smith", age: 20},
  {id: 'ann', name: "Ann Smith", age: 24},
  {id: 'pete', name: "Pete Peterson", age: 31},
];

function groupById(array) {
  return array.reduce((obj, value) => {
    obj[value.id] = value;
    return obj;
  }, {})
}

let usersById = groupById(users);

过滤字谜(需要反复学习)

Anagrams 是具有相同数量相同字母但是顺序不同的单词。

nap - pan 相同字母anp,相同长度,顺序不同
ear - are - era 相同字母aer…
cheaters - hectares - teachers 相同字母aceehrst…

写一个函数 aclean(arr),它返回被清除了字谜(anagrams)的数组。
理解:
字母anp组,只留下一个单词; 字母 aer组只留下一个单词; 字母aceehrst组,只留下一个单词。
对于所有的字谜(anagram)组,都应该保留其中一个词,但保留的具体是哪一个并不重要。

方法1:使用Map

function aclean(arr) {
  let map = new Map();

  for (let word of arr) {
    console.log("原始单词:", word)
    // 将单词 split 成字母,对字母进行排序,之后再 join 回来
    let sorted = word.toLowerCase().split('').sort().join(''); 
    console.log('sorted:', sorted)
    map.set(sorted, word);
  }

  return Array.from(map.values());
}

let arr = ["nap", "teachers", "cheaters", "PAN", "ear", "era", "hectares"];

console.log( aclean(arr) );

如果再次遇到相同字母排序形式的单词,那么它将会覆盖 map 中有相同键的前一个值。

方法2:使用对象Object

function aclean(arr) {
  let obj = {};

  for (let i = 0; i < arr.length; i++) {
    let sorted = arr[i].toLowerCase().split("").sort().join("");
    obj[sorted] = arr[i];
  }

  return Object.values(obj);
}

let arr = ["nap", "teachers", "cheaters", "PAN", "ear", "era", "hectares"];

alert( aclean(arr) );

对象的属性求和

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值