30s-js vscode代码片段

266 阅读33分钟
{
	// Place your snippets for javascript here. Each snippet is defined under a snippet name and has a prefix, body and 
	// description. The prefix is what is used to trigger the snippet and the body will be expanded and inserted. Possible variables are:
	// $1, $2 for tab stops, $0 for the final cursor position, and ${1:label}, ${2:another} for placeholders. Placeholders with the 
	// same ids are connected.
	// Example:
	"Print to console": {
		"prefix": "cl",
		"body": [
			"console.log($1);",
			"$2"
		],
		"description": "Log output to console"
	},
	"all": {
		"prefix": "30s_all",
		"body": [
			"const all = (arr, fn = Boolean) => arr.every(fn);"
		],
		"description": "如果提供的谓词函数为集合中的所有元素返回“true”,则返回“true”,否则返回“false”。 使用`Array.prototype.every()`来测试集合中的所有元素是否返回“true”基于`fn`。省略第二个参数`fn`,使用`Boolean`作为默认值。"
	},
	"allEqual": {
		"prefix": "30s_allEqual",
		"body": [
			"const allEqual = arr => arr.every(val => val === arr[0]);"
		],
		"description": "检查数组中的所有元素是否相等。 使用`Array.prototype.every()`来检查数组的所有元素是否与第一个元素相同。使用严格比较数组中的元素比较运算符,它不考虑“NaN”自我不等式。"
	},
	"any": {
		"prefix": "30s_any",
		"body": [
			"const any = (arr, fn = Boolean) => arr.some(fn);"
		],
		"description": "如果提供的谓词函数为集合中的至少一个元素返回“true”,则返回“true”,否则返回“false”。 使用`Array.prototype.some()`来测试集合中的任何元素是否返回` true`基于`fn`。省略第二个参数`fn`,使用`Boolean`作为默认值。"
	},
	"approximatelyEqual": {
		"prefix": "30s_approximatelyEqual",
		"body": [
			"const approximatelyEqual = (v1, v2, epsilon = 0.001) => Math.abs(v1 - v2) < epsilon;"
		],
		"description": "检查两个数字是否大致相等。 使用`Math.abs()`将两个值的绝对差值与'epsilon`进行比较。忽略第三个参数`epsilon`,使用默认值价值'0.001`。"
	},
	"arrayToCSV": {
		"prefix": "30s_arrayToCSV",
		"body": [
			"const arrayToCSV = (arr, delimiter = ',') =>",
			"  arr",
			"    .map(v => v.map(x => (isNaN(x) ? `\"${x.replace(/\"/g, '\"\"')}\"` : x)).join(delimiter))",
			"    .join('\\n');"
		],
		"description": "将2D数组转换为逗号分隔值(CSV)字符串。 使用`Array.prototype.map()`和`Array.prototype.join(delimiter)`将各个1D数组(行)组合成字符串。 使用`Array.prototype.join(' ')`将所有行组合成一个CSV字符串,用换行符分隔每一行。省略第二个参数`delimiter`,使用`的默认分隔符, `。"
	},
	"arrayToHtmlList": {
		"prefix": "30s_arrayToHtmlList",
		"body": [
			"const arrayToHtmlList = (arr, listID) =>",
			"  (el => (",
			"    (el = document.querySelector('#' + listID)),",
			"    (el.innerHTML += arr.map(item => `<li>${item}</li>`).join(''))",
			"  ))();"
		],
		"description": "将给定的数组元素转换为`<li>`标签并将它们附加到给定id的列表中。 使用`Array.prototype.map()`,`document.querySelector()`和一个匿名内部闭包创建一个html标签列表。"
	},
	"ary": {
		"prefix": "30s_ary",
		"body": [
			"const ary = (fn, n) => (...args) => fn(...args.slice(0, n));"
		],
		"description": "创建一个接受最多`n`参数的函数,忽略任何其他参数。 使用`Array.prototype.slice(0,n)调用提供的函数`fn`,最多为`n`参数。和扩展运算符(`...`)。"
	},
	"atob": {
		"prefix": "30s_atob",
		"body": [
			"const atob = str => Buffer.from(str, 'base64').toString('binary');"
		],
		"description": "解码使用base-64编码编码的数据字符串。 使用base-64编码为给定字符串创建“Buffer”,并使用“Buffer.toString('binary')`返回已解码的字符串。 "
	},
	"attempt": {
		"prefix": "30s_attempt",
		"body": [
			"const attempt = (fn, ...args) => {",
			"  try {",
			"    return fn(...args);",
			"  } catch (e) {",
			"    return e instanceof Error ? e : new Error(e);",
			"  }",
			"};"
		],
		"description": "尝试使用提供的参数调用函数,返回结果或捕获的错误对象。 使用`try ... catch`块返回函数的结果或适当的错误。"
	},
	"average": {
		"prefix": "30s_average",
		"body": [
			"const average = (...nums) => nums.reduce((acc, val) => acc + val, 0) / nums.length;"
		],
		"description": "返回两个或多个数字的平均值。 使用`Array.prototype.reduce()`将每个值添加到累加器,用值“0”初始化,除以数组的“长度”。 ñ"
	},
	"averageBy": {
		"prefix": "30s_averageBy",
		"body": [
			"const averageBy = (arr, fn) =>",
			"  arr.map(typeof fn === 'function' ? fn : val => val[fn]).reduce((acc, val) => acc + val, 0) /",
			"  arr.length;"
		],
		"description": "在使用提供的函数将每个元素映射到一个值之后,返回数组的平均值。 使用`Array.prototype.map()`将每个元素映射到`fn`,`Array.prototype返回的值。 reduce()`将每个值添加到累加器,用值“0”初始化,除以数组的“长度”。"
	},
	"bifurcate": {
		"prefix": "30s_bifurcate",
		"body": [
			"const bifurcate = (arr, filter) =>",
			"  arr.reduce((acc, val, i) => (acc[filter[i] ? 0 : 1].push(val), acc), [[], []]);"
		],
		"description": "将值拆分为两组。如果`filter`中的元素是真实的,则集合中的对应元素属于第一组;否则,它属于第二组。 使用`Array.prototype.reduce()`和`Array.prototype.push()`将组件添加到组中,基于`filter`。"
	},
	"bifurcateBy": {
		"prefix": "30s_bifurcateBy",
		"body": [
			"const bifurcateBy = (arr, fn) =>",
			"  arr.reduce((acc, val, i) => (acc[fn(val, i) ? 0 : 1].push(val), acc), [[], []]);"
		],
		"description": "根据谓词函数将值拆分为两个组,谓词函数指定输入集合中的元素属于哪个组。如果谓词函数返回truthy值,则collection元素属于第一个组;否则,它属于第二组。 使用`Array.prototype.reduce()`和`Array.prototype.push()`将组件添加到组中,基于`fn`为每个元素返回的值。"
	},
	"bind": {
		"prefix": "30s_bind",
		"body": [
			"const bind = (fn, context, ...boundArgs) => (...args) => fn.apply(context, [...boundArgs, ...args]);"
		],
		"description": "创建一个使用给定上下文调用`fn`的函数,可选地将任何其他提供的参数添加到参数的开头。 返回一个使用`Function.prototype.apply()`来应用给定`的``函数` context` to`fn`。使用`Array.prototype.concat()`将任何其他提供的参数添加到参数中。"
	},
	"bindAll": {
		"prefix": "30s_bindAll",
		"body": [
			"const bindAll = (obj, ...fns) =>",
			"  fns.forEach(",
			"    fn => (",
			"      (f = obj[fn]),",
			"      (obj[fn] = function() {",
			"        return f.apply(obj);",
			"      })",
			"    )",
			"  );"
		],
		"description": "将对象的方法绑定到对象本身,覆盖现有方法。 使用`Array.prototype.forEach()`返回一个`function`,它使用`Function.prototype.apply()`来应用给定的上下文对于指定的每个函数,(`obj`)为`fn`。"
	},
	"bindKey": {
		"prefix": "30s_bindKey",
		"body": [
			"const bindKey = (context, fn, ...boundArgs) => (...args) =>",
			"  context[fn].apply(context, [...boundArgs, ...args]);"
		],
		"description": "创建一个在对象的给定键处调用方法的函数,可选地将任何其他提供的参数添加到参数的开头。 返回一个使用`Function.prototype.apply()`绑定`的`function` context [fn]`to`context`。使用扩展运算符(`...`)将任何其他提供的参数添加到参数中。"
	},
	"binomialCoefficient": {
		"prefix": "30s_binomialCoefficient",
		"body": [
			"const binomialCoefficient = (n, k) => {",
			"  if (Number.isNaN(n) || Number.isNaN(k)) return NaN;",
			"  if (k < 0 || k > n) return 0;",
			"  if (k === 0 || k === n) return 1;",
			"  if (k === 1 || k === n - 1) return n;",
			"  if (n - k < k) k = n - k;",
			"  let res = n;",
			"  for (let j = 2; j <= k; j++) res *= (n - j + 1) / j;",
			"  return Math.round(res);",
			"};"
		],
		"description": "计算两个整数“n”和“k”的二项式系数。 使用“Number.isNaN()”来检查两个值中是否有任何一个是“NaN”。如果`k`小于0,请检查`,大于或等于`n`,等于`1`或'n  - 1`并返回相应的结果。如果`n  - k`小于`k`则检查并相应地切换它们的值。Loop从`2`到`k`并计算二项式系数。使用`Math.round()`来计算计算中的舍入误差。"
	},
	"bottomVisible": {
		"prefix": "30s_bottomVisible",
		"body": [
			"const bottomVisible = () =>",
			"  document.documentElement.clientHeight + window.scrollY >=",
			"  (document.documentElement.scrollHeight || document.documentElement.clientHeight);"
		],
		"description": "如果页面底部可见,则返回“true”,否则返回“false”。 使用`scrollY`,`scrollHeight`和`clientHeight`确定页面底部是否可见。"
	},
	"btoa": {
		"prefix": "30s_btoa",
		"body": [
			"const btoa = str => Buffer.from(str, 'binary').toString('base64');"
		],
		"description": "从String对象创建base-64编码的ASCII字符串,其中字符串中的每个字符都被视为二进制数据的字节。 使用二进制编码为给定字符串创建`Buffer`并使用`Buffer.toString( 'base64')`返回编码的字符串。"
	},
	"byteSize": {
		"prefix": "30s_byteSize",
		"body": [
			"const byteSize = str => new Blob([str]).size;"
		],
		"description": "以字节为单位返回字符串的长度。 将给定的字符串转换为[`Blob`对象](https://developer.mozilla.org/en-US/docs/Web/API/Blob)并找到它`size`。"
	},
	"call": {
		"prefix": "30s_call",
		"body": [
			"const call = (key, ...args) => context => context[key](...args);"
		],
		"description": "给定一个键和一组参数,在给定上下文时调用它们。主要用于组合。 使用闭包来调用存储的参数存储的密钥。"
	},
	"capitalize": {
		"prefix": "30s_capitalize",
		"body": [
			"const capitalize = ([first, ...rest], lowerRest = false) =>",
			"  first.toUpperCase() + (lowerRest ? rest.join('').toLowerCase() : rest.join(''));"
		],
		"description": "大写字符串的第一个字母。 使用数组解构和`String.prototype.toUpperCase()`来大写第一个字母,`... rest`以获得第一个字母之后的字符数组,然后是`Array.prototype。 join('')`使它再次成为一个字符串。省略`lowerRest`参数以保持字符串的其余部分不变,或将其设置为`true`以转换为小写。"
	},
	"capitalizeEveryWord": {
		"prefix": "30s_capitalizeEveryWord",
		"body": [
			"const capitalizeEveryWord = str => str.replace(/\\b[a-z]/g, char => char.toUpperCase());"
		],
		"description": "将字符串中每个单词的第一个字母大写。 使用`String.prototype.replace()`匹配每个单词的第一个字符和`String.prototype.toUpperCase()`来大写它。"
	},
	"castArray": {
		"prefix": "30s_castArray",
		"body": [
			"const castArray = val => (Array.isArray(val) ? val : [val]);"
		],
		"description": "如果它不是一个数组,则将提供的值转换为数组。 使用`Array.prototype.isArray()`来确定`val`是否为数组并按原样返回或相应地封装在数组中。"
	},
	"chainAsync": {
		"prefix": "30s_chainAsync",
		"body": [
			"const chainAsync = fns => {",
			"  let curr = 0;",
			"  const last = fns[fns.length - 1];",
			"  const next = () => {",
			"    const fn = fns[curr++];",
			"    fn === last ? fn() : fn(next);",
			"  };",
			"  next();",
			"};"
		],
		"description": "链接异步函数。 通过包含异步事件的函数数组,在每个异步事件完成时调用`next`。"
	},
	"checkProp": {
		"prefix": "30s_checkProp",
		"body": [
			"const checkProp = (predicate, prop) => obj => !!predicate(obj[prop]);"
		],
		"description": "给定一个`predicate`函数和一个`prop`字符串,这个curried函数然后通过调用属性并将它传递给谓词来检查`object`来检查。 在`obj`上的`prop`,传递它提供的`predicate`函数并返回一个屏蔽的布尔值。"
	},
	"chunk": {
		"prefix": "30s_chunk",
		"body": [
			"const chunk = (arr, size) =>",
			"  Array.from({ length: Math.ceil(arr.length / size) }, (v, i) =>",
			"    arr.slice(i * size, i * size + size)",
			"  );"
		],
		"description": "将数组块化为指定大小的较小数组。 使用`Array.from()`创建一个新数组,该数组符合将要生成的块数。使用`Array.prototype.slice()`将新数组的每个元素映射到一个长度为“size”的块。如果原始数组无法均匀分割,则最终的块将包含剩余的元素。"
	},
	"clampNumber": {
		"prefix": "30s_clampNumber",
		"body": [
			"const clampNumber = (num, a, b) => Math.max(Math.min(num, Math.max(a, b)), Math.min(a, b));"
		],
		"description": "在边界值“a”和“b”指定的包含范围内钳位`num`。 如果`num`落在该范围内,则返回`num`。否则,返回范围内最接近的数字。 ñ"
	},
	"cloneRegExp": {
		"prefix": "30s_cloneRegExp",
		"body": [
			"const cloneRegExp = regExp => new RegExp(regExp.source, regExp.flags);"
		],
		"description": "克隆正则表达式。 使用`new RegExp()`,`RegExp.source`和`RegExp.flags`来克隆给定的正则表达式。"
	},
	"coalesce": {
		"prefix": "30s_coalesce",
		"body": [
			"const coalesce = (...args) => args.find(_ => ![undefined, null].includes(_));"
		],
		"description": "返回第一个非null /undefined参数。 使用`Array.prototype.find()`返回第一个非`null` /`undefined`参数。"
	},
	"coalesceFactory": {
		"prefix": "30s_coalesceFactory",
		"body": [
			"const coalesceFactory = valid => (...args) => args.find(valid);"
		],
		"description": "返回一个自定义的coalesce函数,它返回从提供的参数验证函数返回`true`的第一个参数。 使用`Array.prototype.find()`返回从提供的参数验证返回`true`的第一个参数功能。"
	},
	"collectInto": {
		"prefix": "30s_collectInto",
		"body": [
			"const collectInto = fn => (...args) => fn(args);"
		],
		"description": "将接受数组的函数更改为可变函数。 如果给出一个函数,则返回一个闭包,它将所有输入收集到一个接受数组的函数中。"
	},
	"colorize": {
		"prefix": "30s_colorize",
		"body": [
			"const colorize = (...args) => ({",
			"  black: `\\x1b[30m${args.join(' ')}`,",
			"  red: `\\x1b[31m${args.join(' ')}`,",
			"  green: `\\x1b[32m${args.join(' ')}`,",
			"  yellow: `\\x1b[33m${args.join(' ')}`,",
			"  blue: `\\x1b[34m${args.join(' ')}`,",
			"  magenta: `\\x1b[35m${args.join(' ')}`,",
			"  cyan: `\\x1b[36m${args.join(' ')}`,",
			"  white: `\\x1b[37m${args.join(' ')}`,",
			"  bgBlack: `\\x1b[40m${args.join(' ')}\\x1b[0m`,",
			"  bgRed: `\\x1b[41m${args.join(' ')}\\x1b[0m`,",
			"  bgGreen: `\\x1b[42m${args.join(' ')}\\x1b[0m`,",
			"  bgYellow: `\\x1b[43m${args.join(' ')}\\x1b[0m`,",
			"  bgBlue: `\\x1b[44m${args.join(' ')}\\x1b[0m`,",
			"  bgMagenta: `\\x1b[45m${args.join(' ')}\\x1b[0m`,",
			"  bgCyan: `\\x1b[46m${args.join(' ')}\\x1b[0m`,",
			"  bgWhite: `\\x1b[47m${args.join(' ')}\\x1b[0m`",
			"});"
		],
		"description": "在文本中添加特殊字符以在控制台中以彩色打印(与`console.log()`结合使用。) 使用模板文字和特殊字符将相应的颜色代码添加到字符串输出中。对于背景颜色,添加一个特殊字符,用于重置字符串末尾的背景颜色。"
	},
	"compact": {
		"prefix": "30s_compact",
		"body": [
			"const compact = arr => arr.filter(Boolean);"
		],
		"description": "从数组中删除虚假值。 使用`Array.prototype.filter()`来过滤掉虚假值(`false`,`null`,`0`,`\"`,`undefined`,和`NaN`)。"
	},
	"compactWhitespace": {
		"prefix": "30s_compactWhitespace",
		"body": [
			"const compactWhitespace = str => str.replace(/\\s{2,}/g, ' ');"
		],
		"description": "返回一个压缩空格的字符串。 使用带有正则表达式的String.prototype.replace()`将所有出现的2个或更多空格字符替换为单个空格。"
	},
	"compose": {
		"prefix": "30s_compose",
		"body": [
			"const compose = (...fns) => fns.reduce((f, g) => (...args) => f(g(...args)));"
		],
		"description": "执行从右到左的函数组合。 使用`Array.prototype.reduce()`执行从右到左的函数组合。最后(最右边)函数可以接受一个或多个参数;其余的功能必须是一元的。"
	},
	"composeRight": {
		"prefix": "30s_composeRight",
		"body": [
			"const composeRight = (...fns) => fns.reduce((f, g) => (...args) => g(f(...args)));"
		],
		"description": "执行从左到右的函数组合。 使用`Array.prototype.reduce()`执行从左到右的函数组合。第一个(最左边)函数可以接受一个或多个参数;其余的功能必须是一元的。"
	},
	"converge": {
		"prefix": "30s_converge",
		"body": [
			"const converge = (converger, fns) => (...args) => converger(...fns.map(fn => fn.apply(null, args)));"
		],
		"description": "接受聚合函数和分支函数列表,并返回一个函数,将每个分支函数应用于参数,并将分支函数的结果作为参数传递给聚合函数。 使用`Array.prototype.map() `和`Function.prototype.apply()`将每个函数应用于给定的参数。使用扩展运算符(```)用所有其他函数的结果调用`coverger`。"
	},
	"copyToClipboard": {
		"prefix": "30s_copyToClipboard",
		"body": [
			"const copyToClipboard = str => {",
			"  const el = document.createElement('textarea');",
			"  el.value = str;",
			"  el.setAttribute('readonly', '');",
			"  el.style.position = 'absolute';",
			"  el.style.left = '-9999px';",
			"  document.body.appendChild(el);",
			"  const selected =",
			"    document.getSelection().rangeCount > 0 ? document.getSelection().getRangeAt(0) : false;",
			"  el.select();",
			"  document.execCommand('copy');",
			"  document.body.removeChild(el);",
			"  if (selected) {",
			"    document.getSelection().removeAllRanges();",
			"    document.getSelection().addRange(selected);",
			"  }",
			"};"
		],
		"description": "⚠️**注意:**使用新的异步剪贴板API可以轻松实现相同的功能,该API仍然是实验性的,但将来应该使用而不是此片段。了解更多信息[这里](https://github.com/w3c/clipboard-apis/blob/master/explainer.adoc#writing-to-the-clipboard)。 将一个字符串复制到剪贴板。 只能作为用户操作的结果(即在`click`事件监听器内)。 创建一个新的`<textarea>`元素,用提供的数据填充它并将其添加到HTML文档。使用` Selection.getRangeAt()`用于存储所选范围(如果有)。使用`document.execCommand('copy')`复制到剪贴板。从HTML文档中删除`<textarea>`元素。最后,使用`Selection()。addRange()`来恢复原始的选定范围(如果有的话)。"
	},
	"countBy": {
		"prefix": "30s_countBy",
		"body": [
			"const countBy = (arr, fn) =>",
			"  arr.map(typeof fn === 'function' ? fn : val => val[fn]).reduce((acc, val) => {",
			"    acc[val] = (acc[val] || 0) + 1;",
			"    return acc;",
			"  }, {});"
		],
		"description": "基于给定函数对数组的元素进行分组,并返回每个组中元素的数量。 使用`Array.prototype.map()`将数组的值映射到函数或属性名称。使用`Array.prototype.reduce()`用于创建一个对象,其中的键是从映射的结果中生成的。"
	},
	"counter": {
		"prefix": "30s_counter",
		"body": [
			"const counter = (selector, start, end, step = 1, duration = 2000) => {",
			"  let current = start,",
			"    _step = (end - start) * step < 0 ? -step : step,",
			"    timer = setInterval(() => {",
			"      current += _step;",
			"      document.querySelector(selector).innerHTML = current;",
			"      if (current >= end) document.querySelector(selector).innerHTML = end;",
			"      if (current >= end) clearInterval(timer);",
			"    }, Math.abs(Math.floor(duration / (end - start))));",
			"  return timer;",
			"};"
		],
		"description": "创建一个具有指定选择器的指定范围,步长和持续时间的计数器。 检查`step`是否具有正确的符号并相应地进行更改。将`setInterval()`与`Math.abs()结合使用和`Math.floor()`来计算每个新文本绘制之间的时间。使用`document.querySelector()。innerHTML`来更新所选元素的值。省略第四个参数`step`,以便使用默认步骤为“1”。允许第五个参数`duration`使用默认持续时间`2000`ms。"
	},
	"countOccurrences": {
		"prefix": "30s_countOccurrences",
		"body": [
			"const countOccurrences = (arr, val) => arr.reduce((a, v) => (v === val ? a + 1 : a), 0);"
		],
		"description": "计算数组中值的出现次数。 使用`Array.prototype.reduce()`在每次遇到数组内的特定值时递增计数器。"
	},
	"createDirIfNotExists": {
		"prefix": "30s_createDirIfNotExists",
		"body": [
			"const fs = require('fs');",
			"const createDirIfNotExists = dir => (!fs.existsSync(dir) ? fs.mkdirSync(dir) : undefined);"
		],
		"description": "创建一个目录,如果它不存在。 使用`fs.existsSync()`检查目录是否存在,`fs.mkdirSync()`来创建它。"
	},
	"createElement": {
		"prefix": "30s_createElement",
		"body": [
			"const createElement = str => {",
			"  const el = document.createElement('div');",
			"  el.innerHTML = str;",
			"  return el.firstElementChild;",
			"};"
		],
		"description": "从字符串创建元素(不将其附加到文档)。 如果给定的字符串包含多个元素,则只返回第一个元素。 使用`document.createElement()`创建一个新元素。将其`innerHTML`设置为作为参数提供的字符串。 使用`ParentNode.firstElementChild`返回字符串的元素版本。"
	},
	"createEventHub": {
		"prefix": "30s_createEventHub",
		"body": [
			"const createEventHub = () => ({",
			"  hub: Object.create(null),",
			"  emit(event, data) {",
			"    (this.hub[event] || []).forEach(handler => handler(data));",
			"  },",
			"  on(event, handler) {",
			"    if (!this.hub[event]) this.hub[event] = [];",
			"    this.hub[event].push(handler);",
			"  },",
			"  off(event, handler) {",
			"    const i = (this.hub[event] || []).findIndex(h => h === handler);",
			"    if (i > -1) this.hub[event].splice(i, 1);",
			"    if (this.hub[event].length === 0) delete this.hub[event];",
			"  }",
			"});"
		],
		"description": "使用`emit`,`on`和`off`方法创建pub /sub([publish-subscribe](https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern))事件中心。  使用`Object.create(null)`来创建一个空的`hub`对象,该对象不从`Object.prototype`继承属性。对于`emit`,根据`event`参数解析处理程序数组然后通过传入数据作为参数来运行每个`Array.prototype.forEach()`。对于`on`,如果事件尚不存在,则为该事件创建一个数组,然后使用`Array.prototype。 push()`将处理程序添加到数组。对于`off`,使用`Array.prototype.findIndex()`来查找事件数组中处理程序的索引,并使用`Array.prototype.splice删除它()`。"
	},
	"CSVToArray": {
		"prefix": "30s_CSVToArray",
		"body": [
			"const CSVToArray = (data, delimiter = ',', omitFirstRow = false) =>",
			"  data",
			"    .slice(omitFirstRow ? data.indexOf('\\n') + 1 : 0)",
			"    .split('\\n')",
			"    .map(v => v.split(delimiter));"
		],
		"description": "将逗号分隔值(CSV)字符串转换为2D数组。 使用`Array.prototype.slice()`和`Array.prototype.indexOf('')`删除第一行(标题)如果`omitFirstRow`是'true`。使用`String.prototype.split('\\')`为每一行创建一个字符串,然后`String.prototype.split(delimiter)`来分隔值在第一个参数``delimiter`中,使用`,`。的默认分隔符。省略第三个参数`omitFirstRow`,以包含CSV字符串的第一行(标题行)。"
	},
	"CSVToJSON": {
		"prefix": "30s_CSVToJSON",
		"body": [
			"const CSVToJSON = (data, delimiter = ',') => {",
			"  const titles = data.slice(0, data.indexOf('\\n')).split(delimiter);",
			"  return data",
			"    .slice(data.indexOf('\\n') + 1)",
			"    .split('\\n')",
			"    .map(v => {",
			"      const values = v.split(delimiter);",
			"      return titles.reduce((obj, title, index) => ((obj[title] = values[index]), obj), {});",
			"    });",
			"};"
		],
		"description": "将逗号分隔值(CSV)字符串转换为2D对象数组。字符串的第一行用作标题行。 使用“Array.prototype.slice()”和“Array.prototype”。 indexOf('\\')`和`String.prototype.split(delimiter)`将第一行(标题行)分隔为值。使用`String.prototype.split('\\')`来创建每行一个字符串,然后是`Array.prototype.map()`和`String.prototype.split(delimiter)`来分隔每一行中的值。使用`Array.prototype.reduce()`来创建一个对象对于每一行的值,使用从标题行解析的键。省略第二个参数`delimiter`,以使用`,`的默认分隔符。"
	},
	"currentURL": {
		"prefix": "30s_currentURL",
		"body": [
			"const currentURL = () => window.location.href;"
		],
		"description": "返回当前URL。 使用`window.location.href`获取当前URL。"
	},
	"curry": {
		"prefix": "30s_curry",
		"body": [
			"const curry = (fn, arity = fn.length, ...args) =>",
			"  arity <= args.length ? fn(...args) : curry.bind(null, fn, arity, ...args);"
		],
		"description": "为函数提供曲线。 使用递归。如果提供的参数数量(`args`)足够,则调用传递的函数`fn`。否则,返回一个curried函数`fn`,它需要其余的参数。如果你想要一个接受可变数量的参数的函数(一个可变函数,例如`Math.min()`),你可以选择将参数个数传递给第二个参数`arity`。"
	},
	"dayOfYear": {
		"prefix": "30s_dayOfYear",
		"body": [
			"const dayOfYear = date =>",
			"  Math.floor((date - new Date(date.getFullYear(), 0, 0)) / 1000 / 60 / 60 / 24);"
		],
		"description": "从'Date`对象获取一年中的某一天。 使用`new Date()`和`Date.prototype.getFullYear()`来获取一年中的第一天作为`Date`对象,减去它从提供的`date`中除以每天的毫秒数来得到结果。使用`Math.floor()`将得到的日计数适当地舍入为整数。"
	},
	"debounce": {
		"prefix": "30s_debounce",
		"body": [
			"const debounce = (fn, ms = 0) => {",
			"  let timeoutId;",
			"  return function(...args) {",
			"    clearTimeout(timeoutId);",
			"    timeoutId = setTimeout(() => fn.apply(this, args), ms);",
			"  };",
			"};"
		],
		"description": "创建一个去抖动函数,该函数延迟调用所提供的函数,直到自上次调用它以来至少已经过了ms。毫秒。 每次调用去抖动函数时,用`clearTimeout()`清除当前的挂起超时使用`setTimeout()`来创建一个新的超时,延迟调用该函数,直到至少经过`ms`毫秒。使用`Function.prototype.apply()`将`this`上下文应用于函数并提供必要的参数。省略第二个参数`ms`,将超时设置为默认值0 ms。"
	},
	"decapitalize": {
		"prefix": "30s_decapitalize",
		"body": [
			"const decapitalize = ([first, ...rest], upperRest = false) =>",
			"  first.toLowerCase() + (upperRest ? rest.join('').toUpperCase() : rest.join(''));"
		],
		"description": "对字符串的第一个字母进行decapitalize。 使用数组解构和`String.toLowerCase()`来解封第一个字母,`... rest`以获得第一个字母后的字符数组,然后是`Array.prototype.join( '')`再次使它成为一个字符串。省略`upperRest`参数以保持字符串的其余部分不变,或将其设置为`true`以转换为大写。"
	},
	"deepClone": {
		"prefix": "30s_deepClone",
		"body": [
			"const deepClone = obj => {",
			"  let clone = Object.assign({}, obj);",
			"  Object.keys(clone).forEach(",
			"    key => (clone[key] = typeof obj[key] === 'object' ? deepClone(obj[key]) : obj[key])",
			"  );",
			"  return Array.isArray(obj) && obj.length",
			"    ? (clone.length = obj.length) && Array.from(clone)",
			"    : Array.isArray(obj)",
			"    ? Array.from(obj)",
			"    : clone;",
			"};"
		],
		"description": "创建一个对象的深层克隆。 使用递归。使用`Object.assign()`和一个空对象(`{}`)来创建原始的浅层克隆。使用`Object.keys() `和`Array.prototype.forEach()`来确定需要深度克隆的键值对。"
	},
	"deepFlatten": {
		"prefix": "30s_deepFlatten",
		"body": [
			"const deepFlatten = arr => [].concat(...arr.map(v => (Array.isArray(v) ? deepFlatten(v) : v)));"
		],
		"description": "深度展平数组。 使用递归。使用带有空数组(`[]`)的`Array.prototype.concat()`和扩展运算符(`...`)来展平数组。递归地展平作为数组的每个元素。"
	},
	"deepFreeze": {
		"prefix": "30s_deepFreeze",
		"body": [
			"const deepFreeze = obj =>",
			"  Object.keys(obj).forEach(prop =>",
			"    !(obj[prop] instanceof Object) || Object.isFrozen(obj[prop]) ? null : deepFreeze(obj[prop])",
			"  ) || Object.freeze(obj);"
		],
		"description": "深度冻结一个对象。 在递归对象的所有未冻结属性上递归调用`Object.freeze(obj)`,它们是`instanceof`对象。"
	},
	"deepGet": {
		"prefix": "30s_deepGet",
		"body": [
			"const deepGet = (obj, keys) => keys.reduce((xs, x) => (xs && xs[x] ? xs[x] : null), obj);"
		],
		"description": "基于`keys`数组返回嵌套JSON对象中的目标值。 将嵌套JSON对象中所需的键作为“数组”进行比较。使用`Array.prototype.reduce()`获取嵌套JSON对象的值逐个。 如果密钥存在于object中,则返回目标值,否则返回`null`。"
	},
	"deepMapKeys": {
		"prefix": "30s_deepMapKeys",
		"body": [
			"const deepMapKeys = (obj, f) =>",
			"  Array.isArray(obj)",
			"    ? obj.map(val => deepMapKeys(val, f))",
			"    : typeof obj === 'object'",
			"    ? Object.keys(obj).reduce((acc, current) => {",
			"        const val = obj[current];",
			"        acc[f(current)] =",
			"          val !== null && typeof val === 'object' ? deepMapKeys(val, f) : (acc[f(current)] = val);",
			"        return acc;",
			"      }, {})",
			"    : obj;"
		],
		"description": "深度映射对象的键。 使用与提供的对象相同的值创建一个对象,并通过为每个键运行提供的函数生成键。使用`Object.keys(obj)`迭代对象的键。 使用`Array.prototype.reduce()`使用`fn`创建一个具有相同值和映射键的新对象。"
	},
	"defaults": {
		"prefix": "30s_defaults",
		"body": [
			"const defaults = (obj, ...defs) => Object.assign({}, obj, ...defs.reverse(), obj);"
		],
		"description": "为一个对象中的所有属性分配“未定义”的默认值。 使用`Object.assign()`创建一个新的空对象并复制原始对象以维护键顺序,使用`Array.prototype.reverse( )`和扩展运算符`...`从左到右组合默认值,最后再次使用`obj`来覆盖最初有值的属性。"
	},
	"defer": {
		"prefix": "30s_defer",
		"body": [
			"const defer = (fn, ...args) => setTimeout(fn, 1, ...args);"
		],
		"description": "延迟调用函数直到当前调用堆栈清除。 使用超时为1ms的`setTimeout()`将新事件添加到浏览器事件队列并允许渲染引擎完成其工作。使用spread(`...`)运算符为函数提供任意数量的参数。"
	},
	"degreesToRads": {
		"prefix": "30s_degreesToRads",
		"body": [
			"const degreesToRads = deg => (deg * Math.PI) / 180.0;"
		],
		"description": "将角度从度数转换为弧度。 使用“Math.PI”和度数弧度公式将角度从度数转换为弧度。"
	},
	"delay": {
		"prefix": "30s_delay",
		"body": [
			"const delay = (fn, wait, ...args) => setTimeout(fn, wait, ...args);"
		],
		"description": "在`wait`毫秒之后调用提供的函数。 使用`setTimeout()`来延迟执行`fn`。使用spread(```)运算符为函数提供任意数量的参数。 "
	},
	"detectDeviceType": {
		"prefix": "30s_detectDeviceType",
		"body": [
			"const detectDeviceType = () =>",
			"  /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)",
			"    ? 'Mobile'",
			"    : 'Desktop';"
		],
		"description": "检测网站是在移动设备还是桌面/笔记本电脑中打开。 使用正则表达式测试`navigator.userAgent`属性,以确定设备是移动设备还是台式机/笔记本电脑。 ñ"
	},
	"difference": {
		"prefix": "30s_difference",
		"body": [
			"const difference = (a, b) => {",
			"  const s = new Set(b);",
			"  return a.filter(x => !s.has(x));",
			"};"
		],
		"description": "返回两个数组之间的差异。 从`b`创建`Set`,然后在`a`上使用`Array.prototype.filter()`只保留`b`中不包含的值。"
	},
	"differenceBy": {
		"prefix": "30s_differenceBy",
		"body": [
			"const differenceBy = (a, b, fn) => {",
			"  const s = new Set(b.map(fn));",
			"  return a.map(fn).filter(el => !s.has(el));",
			"};"
		],
		"description": "在将提供的函数应用于两者的每个数组元素之后,返回两个数组之间的差异。 通过将`fn`应用于`b`中的每个元素来创建`Set`,然后使用`Array.prototype.map() `将`fn`应用于`a`中的每个元素,然后`Array.prototype.filter()`"
	},
	"differenceWith": {
		"prefix": "30s_differenceWith",
		"body": [
			"const differenceWith = (arr, val, comp) => arr.filter(a => val.findIndex(b => comp(a, b)) === -1);"
		],
		"description": "过滤掉比较器函数未返回“true”的数组中的所有值。 使用`Array.prototype.filter()`和`Array.prototype.findIndex()`来查找适当的值。"
	},
	"dig": {
		"prefix": "30s_dig",
		"body": [
			"const dig = (obj, target) =>",
			"  target in obj",
			"    ? obj[target]",
			"    : Object.values(obj).reduce((acc, val) => {",
			"        if (acc !== undefined) return acc;",
			"        if (typeof val === 'object') return dig(val, target);",
			"      }, undefined);"
		],
		"description": "基于给定的键返回嵌套JSON对象中的目标值。 使用`in`运算符检查`obj`中是否存在`target`。如果找到,则返回`obj [target]的值`,否则使用`Object.values(obj)`和`Array.prototype.reduce()`递归调用每个嵌套对象上的`dig`,直到找到第一个匹配的键/值对。"
	},
	"digitize": {
		"prefix": "30s_digitize",
		"body": [
			"const digitize = n => [...`${n}`].map(i => parseInt(i));"
		],
		"description": "将数字转换为数字数组。 将数字转换为字符串,使用扩展运算符(`...`)构建数组。使用`Array.prototype.map()`和`parseInt( )`将每个值转换为整数。"
	},
	"distance": {
		"prefix": "30s_distance",
		"body": [
			"const distance = (x0, y0, x1, y1) => Math.hypot(x1 - x0, y1 - y0);"
		],
		"description": "返回两点之间的距离。 使用`Math.hypot()`计算两点之间的欧几里德距离。"
	},
	"drop": {
		"prefix": "30s_drop",
		"body": [
			"const drop = (arr, n = 1) => arr.slice(n);"
		],
		"description": "返回一个新的数组,从左边删除了`n`个元素。 使用`Array.prototype.slice()`从左边删除指定数量的元素。"
	},
	"dropRight": {
		"prefix": "30s_dropRight",
		"body": [
			"const dropRight = (arr, n = 1) => arr.slice(0, -n);"
		],
		"description": "返回一个新的数组,从右边删除了`n`个元素。 使用`Array.prototype.slice()`从右边删除指定数量的元素。"
	},
	"dropRightWhile": {
		"prefix": "30s_dropRightWhile",
		"body": [
			"const dropRightWhile = (arr, func) => {",
			"  let rightIndex = arr.length;",
			"  while (rightIndex-- && !func(arr[rightIndex]));",
			"  return arr.slice(0, rightIndex + 1);",
			"};"
		],
		"description": "从数组末尾删除元素,直到传递的函数返回“true”。返回数组中的其余元素。 通过数组,使用`Array.prototype.slice()`删除数组的最后一个元素,直到函数的返回值为“true”。返回剩余的元素元件。"
	},
	"dropWhile": {
		"prefix": "30s_dropWhile",
		"body": [
			"const dropWhile = (arr, func) => {",
			"  while (arr.length > 0 && !func(arr[0])) arr = arr.slice(1);",
			"  return arr;",
			"};"
		],
		"description": "删除数组中的元素,直到传递的函数返回“true”。返回数组中的其余元素。 通过数组,使用`Array.prototype.slice()`删除数组的第一个元素,直到函数的返回值为“true”。返回剩余的元素元件。"
	},
	"elementContains": {
		"prefix": "30s_elementContains",
		"body": [
			"const elementContains = (parent, child) => parent !== child && parent.contains(child);"
		],
		"description": "如果`parent`元素包含`child`元素,则返回`true`,否则返回`false`。 检查`parent`与`child`不是同一元素,使用`parent.contains(child)`检查`parent`元素是否包含`child`元素。"
	},
	"elementIsVisibleInViewport": {
		"prefix": "30s_elementIsVisibleInViewport",
		"body": [
			"const elementIsVisibleInViewport = (el, partiallyVisible = false) => {",
			"  const { top, left, bottom, right } = el.getBoundingClientRect();",
			"  const { innerHeight, innerWidth } = window;",
			"  return partiallyVisible",
			"    ? ((top > 0 && top < innerHeight) || (bottom > 0 && bottom < innerHeight)) &&",
			"        ((left > 0 && left < innerWidth) || (right > 0 && right < innerWidth))",
			"    : top >= 0 && left >= 0 && bottom <= innerHeight && right <= innerWidth;",
			"};"
		],
		"description": "如果指定的元素在视口中可见,则返回“true”,否则返回“false”。 使用`Element.getBoundingClientRect()`和`window.inner(Width | Height)`值来确定给定元素是否在视口中可见。省略第二个参数以确定元素是否完全可见,或指定“true”以确定it是否部分可见。"
	},
	"elo": {
		"prefix": "30s_elo",
		"body": [
			"const elo = ([...ratings], kFactor = 32, selfRating) => {",
			"  const [a, b] = ratings;",
			"  const expectedScore = (self, opponent) => 1 / (1 + 10 ** ((opponent - self) / 400));",
			"  const newRating = (rating, i) =>",
			"    (selfRating || rating) + kFactor * (i - expectedScore(i ? a : b, i ? b : a));",
			"  if (ratings.length === 2) return [newRating(a, 1), newRating(b, 0)];",
			"",
			"  for (let i = 0, len = ratings.length; i < len; i++) {",
			"    let j = i;",
			"    while (j < len - 1) {",
			"      j++;",
			"      [ratings[i], ratings[j]] = elo([ratings[i], ratings[j]], kFactor);",
			"    }",
			"  }",
			"  return ratings;",
			"};"
		],
		"description": "使用[Elo评级系统](https://en.wikipedia.org/wiki/Elo_rating_system)计算两个或更多对手之间的新评级。它需要一个数组of预评级并返回一个包含后评级的数组。数组应该从最佳表演者到最差表演者(赢家 ->输家)排序。 使用指数`**'运算符和数学运营商计算每个对手的预期得分(获胜机会)。并计算每个对手的新评级。通过评级,使用每个排列以成对方式计算每个玩家的后Elo评级。 允许第二个参数使用32. 的默认`kFactor`"
	},
	"equals": {
		"prefix": "30s_equals",
		"body": [
			"const equals = (a, b) => {",
			"  if (a === b) return true;",
			"  if (a instanceof Date && b instanceof Date) return a.getTime() === b.getTime();",
			"  if (!a || !b || (typeof a !== 'object' && typeof b !== 'object')) return a === b;",
			"  if (a === null || a === undefined || b === null || b === undefined) return false;",
			"  if (a.prototype !== b.prototype) return false;",
			"  let keys = Object.keys(a);",
			"  if (keys.length !== Object.keys(b).length) return false;",
			"  return keys.every(k => equals(a[k], b[k]));",
			"};"
		],
		"description": "在两个值之间进行深度比较,以确定它们是否相等。 检查两个值是否相同,是否同时具有“Date”对象,使用`Date.getTime()`或者它们是否相同具有等效值的非对象值(严格比较)。如果只有一个值为“null”或“undefined”或者它们的原型不同,则检查它们。如果没有满足上述条件,请使用`Object.keys( )`检查两个值是否具有相同数量的键,然后使用`Array.prototype.every()`来检查第一个值中的每个键是否都存在于第二个值中,如果它们是等效的,则通过递归调用此方法。 "
	},
	"escapeHTML": {
		"prefix": "30s_escapeHTML",
		"body": [
			"const escapeHTML = str =>",
			"  str.replace(",
			"    /[&<>'\"]/g,",
			"    tag =>",
			"      ({",
			"        '&': '&amp;',",
			"        '<': '&lt;',",
			"        '>': '&gt;',",
			"        \"'\": '&#39;',",
			"        '\"': '&quot;'",
			"      }[tag] || tag)",
			"  );"
		],
		"description": "转义一个字符串以便在HTML中使用。 使用`String.prototype.replace()`,其regexp与需要转义的字符匹配,使用回调函数将每个字符实例替换为其关联的转义字符字典(对象)。"
	},
	"escapeRegExp": {
		"prefix": "30s_escapeRegExp",
		"body": [
			"const escapeRegExp = str => str.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');"
		],
		"description": "转义要在正则表达式中使用的字符串。 使用`String.prototype.replace()`来转义特殊字符。"
	},
	"everyNth": {
		"prefix": "30s_everyNth",
		"body": [
			"const everyNth = (arr, nth) => arr.filter((e, i) => i % nth === nth - 1);"
		],
		"description": "返回数组中的每个第n个元素。 使用`Array.prototype.filter()`创建一个包含给定数组的每个第n个元素的新数组。"
	},
	"extendHex": {
		"prefix": "30s_extendHex",
		"body": [
			"const extendHex = shortHex =>",
			"  '#' +",
			"  shortHex",
			"    .slice(shortHex.startsWith('#') ? 1 : 0)",
			"    .split('')",
			"    .map(x => x + x)",
			"    .join('');"
		],
		"description": "将3位颜色代码扩展为6位颜色代码。 使用`Array.prototype.map()`,`String.prototype.split()`和`Array.prototype.join()`进行连接用于将3位RGB标记的十六进制颜色代码转换为6位数字形式的映射数组。`Array.prototype.slice()`用于从字符串start中删除`#`,因为它已添加一次。"
	},
	"factorial": {
		"prefix": "30s_factorial",
		"body": [
			"const factorial = n =>",
			"  n < 0",
			"    ? (() => {",
			"        throw new TypeError('Negative numbers are not allowed!');",
			"      })()",
			"    : n <= 1",
			"    ? 1",
			"    : n * factorial(n - 1);"
		],
		"description": "计算一个数的阶乘。 使用递归。如果`n`小于或等于`1`,则返回`1`。否则,返回`n`的乘积和`n的阶乘 -1`。如果`n`是负数,则表示异常。"
	},
	"fibonacci": {
		"prefix": "30s_fibonacci",
		"body": [
			"const fibonacci = n =>",
			"  Array.from({ length: n }).reduce(",
			"    (acc, val, i) => acc.concat(i > 1 ? acc[i - 1] + acc[i - 2] : i),",
			"    []",
			"  );"
		],
		"description": "生成一个包含Fibonacci序列的数组,直到第n个项。 创建一个特定长度的空数组,初始化前两个值(“0”和“1”)。使用`Array.prototype.reduce ()`使用最后两个值的总和将值添加到数组中,前两个值除外。"
	},
	"filterFalsy": {
		"prefix": "30s_filterFalsy",
		"body": [
			"const filterFalsy = arr => arr.filter(Boolean);"
		],
		"description": "过滤掉数组中的虚假值。 使用`Array.prototype.filter()`获取仅包含truthy值的数组。"
	},
	"filterNonUnique": {
		"prefix": "30s_filterNonUnique",
		"body": [
			"const filterNonUnique = arr => arr.filter(i => arr.indexOf(i) === arr.lastIndexOf(i));"
		],
		"description": "过滤掉数组中的非唯一值。 使用`Array.prototype.filter()`获取仅包含唯一值的数组。"
	},
	"filterNonUniqueBy": {
		"prefix": "30s_filterNonUniqueBy",
		"body": [
			"const filterNonUniqueBy = (arr, fn) =>",
			"  arr.filter((v, i) => arr.every((x, j) => (i === j) === fn(v, x, i, j)));"
		],
		"description": "根据提供的比较器函数过滤掉数组中的非唯一值。 使用`Array.prototype.filter()`和`Array.prototype.every()`表示只包含唯一值的数组,基于比较器函数,`fn`。比较器函数有四个参数:被比较的两个元素的值及其索引。"
	},
	"findKey": {
		"prefix": "30s_findKey",
		"body": [
			"const findKey = (obj, fn) => Object.keys(obj).find(key => fn(obj[key], key, obj));"
		],
		"description": "返回满足提供的测试函数的第一个键。否则返回`undefined`。 使用`Object.keys(obj)`获取对象的所有属性,`Array.prototype.find()`来测试每个键值对提供的函数。回调接收三个参数 -值,键和对象。"
	},
	"findLast": {
		"prefix": "30s_findLast",
		"body": [
			"const findLast = (arr, fn) => arr.filter(fn).pop();"
		],
		"description": "返回提供的函数返回truthy值的最后一个元素。 使用`Array.prototype.filter()`删除`fn`返回falsy值的元素,`Array.prototype.pop()`得到最后一个。"
	},
	"findLastIndex": {
		"prefix": "30s_findLastIndex",
		"body": [
			"const findLastIndex = (arr, fn) =>",
			"  (arr",
			"    .map((val, i) => [i, val])",
			"    .filter(([i, val]) => fn(val, i, arr))",
			"    .pop() || [-1])[0];"
		],
		"description": "返回提供的函数返回truthy值的最后一个元素的索引。 使用`Array.prototype.map()`将每个元素映射到具有索引和值的数组。使用`Array.prototype。 filter()`删除`fn`返回falsy值的元素,`Array.prototype.pop()`以获取最后一个。`-1`是未找到时的默认值。"
	},
	"findLastKey": {
		"prefix": "30s_findLastKey",
		"body": [
			"const findLastKey = (obj, fn) =>",
			"  Object.keys(obj)",
			"    .reverse()",
			"    .find(key => fn(obj[key], key, obj));"
		],
		"description": "返回满足提供的测试函数的最后一个键。否则返回`undefined`。 使用`Object.keys(obj)`获取对象的所有属性,`Array.prototype.reverse()`to反转它们的顺序和`Array.prototype.find()`来测试每个键值对提供的函数。回调接收三个参数 -值,键和对象。"
	},
	"flatten": {
		"prefix": "30s_flatten",
		"body": [
			"const flatten = (arr, depth = 1) =>",
			"  arr.reduce((a, v) => a.concat(depth > 1 && Array.isArray(v) ? flatten(v, depth - 1) : v), []);"
		],
		"description": "将数组展平到指定的深度。 使用递归,为每个深度级别将“深度”减1。使用`Array.prototype.reduce()`和`Array.prototype.concat()`进行合并元素或数组。基本情况,对于`深度`等于`1`停止递归。省略第二个参数,`depth`仅展平为“1”的深度(单个展平)。"
	},
	"flattenObject": {
		"prefix": "30s_flattenObject",
		"body": [
			"const flattenObject = (obj, prefix = '') =>",
			"  Object.keys(obj).reduce((acc, k) => {",
			"    const pre = prefix.length ? prefix + '.' : '';",
			"    if (typeof obj[k] === 'object') Object.assign(acc, flattenObject(obj[k], pre + k));",
			"    else acc[pre + k] = obj[k];",
			"    return acc;",
			"  }, {});"
		],
		"description": "使用键的路径展平对象。 使用递归。使用`Object.keys(obj)`结合`Array.prototype.reduce()`将每个叶节点转换为展平的路径节点。如果键的值是一个对象,该函数使用相应的`prefix`调用自身以使用`Object.assign()`创建路径。否则,它会将相应的前缀键值对添加到累加器对象。您应该总是省略第二个参数`prefix`,除非你希望每个键都有一个前缀。"
	},
	"flip": {
		"prefix": "30s_flip",
		"body": [
			"const flip = fn => (first, ...rest) => fn(...rest, first);"
		],
		"description": "Flip将一个函数作为参数,然后将第一个参数作为最后一个。 返回一个采用可变参数输入的闭包,并拼接最后一个参数,使其成为应用其余参数之前的第一个参数。"
	},
	"forEachRight": {
		"prefix": "30s_forEachRight",
		"body": [
			"const forEachRight = (arr, callback) =>",
			"  arr",
			"    .slice(0)",
			"    .reverse()",
			"    .forEach(callback);"
		],
		"description": "从数组的最后一个元素开始为每个数组元素执行一次提供的函数。 使用`Array.prototype.slice(0)`来克隆给定的数组,`Array.prototype.reverse()`来反转它和`Array.prototype.forEach()`迭代反转数组。"
	},
	"formatDuration": {
		"prefix": "30s_formatDuration",
		"body": [
			"const formatDuration = ms => {",
			"  if (ms < 0) ms = -ms;",
			"  const time = {",
			"    day: Math.floor(ms / 86400000),",
			"    hour: Math.floor(ms / 3600000) % 24,",
			"    minute: Math.floor(ms / 60000) % 60,",
			"    second: Math.floor(ms / 1000) % 60,",
			"    millisecond: Math.floor(ms) % 1000",
			"  };",
			"  return Object.entries(time)",
			"    .filter(val => val[1] !== 0)",
			"    .map(([key, val]) => `${val} ${key}${val !== 1 ? 's' : ''}`)",
			"    .join(', ');",
			"};"
		],
		"description": "返回给定毫秒数的人类可读格式。 使用适当的值分割“ms”以获取“day”,“hour”,“minute”,“second”和“millisecond”的相应值。 n使用`Array.entries()`和`Array.prototype.filter()`来保持非零值。使用`Array.prototype.map()`为每个值创建字符串,适当地复数。使用`String.prototype.join(',')`将值组合成一个字符串。"
	},
	"formToObject": {
		"prefix": "30s_formToObject",
		"body": [
			"const formToObject = form =>",
			"  Array.from(new FormData(form)).reduce(",
			"    (acc, [key, value]) => ({",
			"      ...acc,",
			"      [key]: value",
			"    }),",
			"    {}",
			"  );"
		],
		"description": "将一组表单元素编码为`object`。 使用`FormData`构造函数将HTML`表单`转换为`FormData`,`Array.from()`以转换为数组。收集对象从数组中,使用`Array.prototype.reduce()`。"
	},
	"forOwn": {
		"prefix": "30s_forOwn",
		"body": [
			"const forOwn = (obj, fn) => Object.keys(obj).forEach(key => fn(obj[key], key, obj));"
		],
		"description": "迭代一个对象的所有属性,为每个属性运行一个回调。 使用`Object.keys(obj)`来获取对象的所有属性,`Array.prototype.forEach()`来运行提供的每个键值对的函数。回调接收三个参数 -值,键和对象。"
	},
	"forOwnRight": {
		"prefix": "30s_forOwnRight",
		"body": [
			"const forOwnRight = (obj, fn) =>",
			"  Object.keys(obj)",
			"    .reverse()",
			"    .forEach(key => fn(obj[key], key, obj));"
		],
		"description": "反过来迭代一个对象的所有属性,为每个属性运行一个回调。 使用`Object.keys(obj)`来获取对象的所有属性,`Array.prototype.reverse()`来反转他们的命令和`Array.prototype.forEach()`为每个键值对运行提供的函数。回调接收三个参数 -值,键和对象。"
	},
	"fromCamelCase": {
		"prefix": "30s_fromCamelCase",
		"body": [
			"const fromCamelCase = (str, separator = '_') =>",
			"  str",
			"    .replace(/([a-z\\d])([A-Z])/g, '$1' + separator + '$2')",
			"    .replace(/([A-Z]+)([A-Z][a-z\\d]+)/g, '$1' + separator + '$2')",
			"    .toLowerCase();"
		],
		"description": "转换来自camelcase的字符串。 使用`String.prototype.replace()`删除下划线,连字符和空格并将单词转换为camelcase。允许第二个参数使用`_`的默认`separator`。 "
	},
	"functionName": {
		"prefix": "30s_functionName",
		"body": [
			"const functionName = fn => (console.debug(fn.name), fn);"
		],
		"description": "记录函数的名称。 使用`console.debug()`和传递方法的`name`属性将方法名称记录到控制台的`debug`通道。"
	},
	"functions": {
		"prefix": "30s_functions",
		"body": [
			"const functions = (obj, inherited = false) =>",
			"  (inherited",
			"    ? [...Object.keys(obj), ...Object.keys(Object.getPrototypeOf(obj))]",
			"    : Object.keys(obj)",
			"  ).filter(key => typeof obj[key] === 'function');"
		],
		"description": "从对象的自身(和可选继承)可枚举属性返回一个函数属性名称数组。 使用`Object.keys(obj)`迭代对象自己的属性。如果`inherited`是`true`,使用`Object.get.PrototypeOf(obj)`来获取对象的继承属性。使用`Array.prototype.filter()`只保留那些属于函数的属性。省略第二个参数`inherited`到默认情况下不包括继承的属性。"
	},
	"gcd": {
		"prefix": "30s_gcd",
		"body": [
			"const gcd = (...arr) => {",
			"  const _gcd = (x, y) => (!y ? x : gcd(y, x % y));",
			"  return [...arr].reduce((a, b) => _gcd(a, b));",
			"};"
		],
		"description": "计算两个或多个数字/数组之间的最大公约数。 内部`_gcd`函数使用递归。Base情况是`y`等于`0`。在这种情况下,返回`x`。否则,返回`y`的GCD和除法的剩余部分`x /y`。"
	},
	"geometricProgression": {
		"prefix": "30s_geometricProgression",
		"body": [
			"const geometricProgression = (end, start = 1, step = 2) =>",
			"  Array.from({ length: Math.floor(Math.log(end / start) / Math.log(step)) + 1 }).map(",
			"    (v, i) => start * step ** i",
			"  );"
		],
		"description": "初始化一个数组,其中包含指定范围内的数字,其中`start`和`end`是包含的,两个术语之间的比率是`step`。如果`step`等于`1`则返回错误。 使用`数组.from()`,`Math.log()`和`Math.floor()`来创建一个所需长度的数组,`Array.prototype.map()`来填充一个范围内的所需值。 n省略第二个参数`start`,使用默认值`1`。忽略第三个参数`step`,使用默认值`2`。"
	},
	"get": {
		"prefix": "30s_get",
		"body": [
			"const get = (from, ...selectors) =>",
			"  [...selectors].map(s =>",
			"    s",
			"      .replace(/\\[([^\\[\\]]*)\\]/g, '.$1.')",
			"      .split('.')",
			"      .filter(t => t !== '')",
			"      .reduce((prev, cur) => prev && prev[cur], from)",
			"  );"
		],
		"description": "从对象中检索由给定选择器指示的一组属性。 对于每个选择器使用`Array.prototype.map()`,`String.prototype.replace()`用点替换方括号,`String。 prototype.split('。')`拆分每个选择器,`Array.prototype.filter()`删除空值和`Array.prototype.reduce()`来获取它指示的值。"
	},
	"getColonTimeFromDate": {
		"prefix": "30s_getColonTimeFromDate",
		"body": [
			"const getColonTimeFromDate = date => date.toTimeString().slice(0, 8);"
		],
		"description": "从`Date`对象返回一个形式为'HH:MM:SS`的字符串。 使用`Date.prototype.toTimeString()`和`String.prototype.slice()`得到`HH:MM :SS`给定`Date`对象的一部分。"
	},
	"getDaysDiffBetweenDates": {
		"prefix": "30s_getDaysDiffBetweenDates",
		"body": [
			"const getDaysDiffBetweenDates = (dateInitial, dateFinal) =>",
			"  (dateFinal - dateInitial) / (1000 * 3600 * 24);"
		],
		"description": "返回两个日期之间的差异(以天为单位)。 计算两个“Date”对象之间的差异(以天为单位)。"
	},
	"getImages": {
		"prefix": "30s_getImages",
		"body": [
			"const getImages = (el, includeDuplicates = false) => {",
			"  const images = [...el.getElementsByTagName('img')].map(img => img.getAttribute('src'));",
			"  return includeDuplicates ? images : [...new Set(images)];",
			"};"
		],
		"description": "从元素中获取所有图像并将它们放入数组中 使用`Element.prototype.getElementsByTagName()`来获取所提供元素内的所有`<img>`元素,`Array.prototype.map()`映射各自`<img>`元素的每个`src`属性,然后创建一个`Set`来消除重复并返回数组。"
	},
	"getMeridiemSuffixOfInteger": {
		"prefix": "30s_getMeridiemSuffixOfInteger",
		"body": [
			"const getMeridiemSuffixOfInteger = num =>",
			"  num === 0 || num === 24",
			"    ? 12 + 'am'",
			"    : num === 12",
			"    ? 12 + 'pm'",
			"    : num < 12",
			"    ? (num % 12) + 'am'",
			"    : (num % 12) + 'pm';"
		],
		"description": "将整数转换为后缀字符串,根据其值添加“am”或“pm”。 使用模运算符(`%`)和条件检查将整数转换为带有meridiem后缀的字符串化12小时格式。"
	},
	"getScrollPosition": {
		"prefix": "30s_getScrollPosition",
		"body": [
			"const getScrollPosition = (el = window) => ({",
			"  x: el.pageXOffset !== undefined ? el.pageXOffset : el.scrollLeft,",
			"  y: el.pageYOffset !== undefined ? el.pageYOffset : el.scrollTop",
			"});"
		],
		"description": "返回当前页面的滚动位置。 如果定义了`pageXOffset`和`pageYOffset`,否则使用`scrollLeft`和`scrollTop`。你可以省略`el`来使用`window`的默认值。"
	},
	"getStyle": {
		"prefix": "30s_getStyle",
		"body": [
			"const getStyle = (el, ruleName) => getComputedStyle(el)[ruleName];"
		],
		"description": "返回指定元素的CSS规则的值。 使用`Window.getComputedStyle()`获取指定元素的CSS规则的值。"
	},
	"getType": {
		"prefix": "30s_getType",
		"body": [
			"const getType = v =>",
			"  v === undefined ? 'undefined' : v === null ? 'null' : v.constructor.name.toLowerCase();"
		],
		"description": "返回值的本机类型。 返回小写的构造函数名称,`undefined`或`null`如果value是'undefined`或`null`。"
	},
	"getURLParameters": {
		"prefix": "30s_getURLParameters",
		"body": [
			"const getURLParameters = url =>",
			"  (url.match(/([^?=&]+)(=([^&]*))/g) || []).reduce(",
			"    (a, v) => ((a[v.slice(0, v.indexOf('='))] = v.slice(v.indexOf('=') + 1)), a),",
			"    {}",
			"  );"
		],
		"description": "返回一个包含当前URL参数的对象。 使用`String.match()`使用适当的正则表达式获取所有键值对,使用`Array.prototype.reduce()`映射并将它们组合成单个对象。请将`location.search`作为应用于当前`url`的参数。"
	},
	"groupBy": {
		"prefix": "30s_groupBy",
		"body": [
			"const groupBy = (arr, fn) =>",
			"  arr.map(typeof fn === 'function' ? fn : val => val[fn]).reduce((acc, val, i) => {",
			"    acc[val] = (acc[val] || []).concat(arr[i]);",
			"    return acc;",
			"  }, {});"
		],
		"description": "基于给定函数对数组的元素进行分组。 使用`Array.prototype.map()`将数组的值映射到函数或属性名。使用`Array.prototype.reduce()`创建一个对象,其中的键是从映射的结果中生成的。"
	},
	"hammingDistance": {
		"prefix": "30s_hammingDistance",
		"body": [
			"const hammingDistance = (num1, num2) => ((num1 ^ num2).toString(2).match(/1/g) || '').length;"
		],
		"description": "计算两个值之间的汉明距离。 使用XOR运算符(`^`)找到两个数字之间的位差,使用`toString(2)`。Count转换为二进制字符串并返回数字`字符串中的1个,使用`match(/1 /g)`。"
	},
	"hasClass": {
		"prefix": "30s_hasClass",
		"body": [
			"const hasClass = (el, className) => el.classList.contains(className);"
		],
		"description": "如果元素具有指定的类,则返回“true”,否则返回“false”。 使用`element.classList.contains()`来检查元素是否具有指定的类。"
	},
	"hasFlags": {
		"prefix": "30s_hasFlags",
		"body": [
			"const hasFlags = (...flags) =>",
			"  flags.every(flag => process.argv.includes(/^-{1,2}/.test(flag) ? flag : '--' + flag));"
		],
		"description": "检查当前进程的参数是否包含指定的标志。 使用`Array.prototype.every()`和`Array.prototype.includes()`检查`process.argv`是否包含所有指定的标志。使用一个正则表达式,用于测试指定的标志是否以`-`或` -`作为前缀,并相应地加上前缀。"
	},
	"hashBrowser": {
		"prefix": "30s_hashBrowser",
		"body": [
			"const hashBrowser = val =>",
			"  crypto.subtle.digest('SHA-256', new TextEncoder('utf-8').encode(val)).then(h => {",
			"    let hexes = [],",
			"      view = new DataView(h);",
			"    for (let i = 0; i < view.byteLength; i += 4)",
			"      hexes.push(('00000000' + view.getUint32(i).toString(16)).slice(-8));",
			"    return hexes.join('');",
			"  });"
		],
		"description": "使用[SHA-256](https://en.wikipedia.org/wiki/SHA-2)算法为值创建哈希值。返回一个promise。 使用[SubtleCrypto](https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto)API为给定值创建一个哈希值。"
	},
	"hashNode": {
		"prefix": "30s_hashNode",
		"body": [
			"const crypto = require('crypto');",
			"const hashNode = val =>",
			"  new Promise(resolve =>",
			"    setTimeout(",
			"      () =>",
			"        resolve(",
			"          crypto",
			"            .createHash('sha256')",
			"            .update(val)",
			"            .digest('hex')",
			"        ),",
			"      0",
			"    )",
			"  );"
		],
		"description": "使用[SHA-256](https://en.wikipedia.org/wiki/SHA-2)算法为值创建哈希值。返回一个promise。 使用`crypto` API为给定值创建一个哈希,`setTimeout`用来阻止长操作的阻塞,一个`Promise`给它一个熟悉的接口。"
	},
	"head": {
		"prefix": "30s_head",
		"body": [
			"const head = arr => arr[0];"
		],
		"description": "返回列表的头部。 使用`arr [0]`返回传递的数组的第一个元素。"
	},
	"hexToRGB": {
		"prefix": "30s_hexToRGB",
		"body": [
			"const hexToRGB = hex => {",
			"  let alpha = false,",
			"    h = hex.slice(hex.startsWith('#') ? 1 : 0);",
			"  if (h.length === 3) h = [...h].map(x => x + x).join('');",
			"  else if (h.length === 8) alpha = true;",
			"  h = parseInt(h, 16);",
			"  return (",
			"    'rgb' +",
			"    (alpha ? 'a' : '') +",
			"    '(' +",
			"    (h >>> (alpha ? 24 : 16)) +",
			"    ', ' +",
			"    ((h & (alpha ? 0x00ff0000 : 0x00ff00)) >>> (alpha ? 16 : 8)) +",
			"    ', ' +",
			"    ((h & (alpha ? 0x0000ff00 : 0x0000ff)) >>> (alpha ? 8 : 0)) +",
			"    (alpha ? `, ${h & 0x000000ff}` : '') +",
			"    ')'",
			"  );",
			"};"
		],
		"description": "如果提供了alpha值,则将颜色代码转换为`rgb()`或`rgba()`字符串。 使用带有`&`(和)运算符的按位右移运算符和掩码位来转换十六进制颜色代码(带或不带前缀`#`)到带有RGB值的字符串。如果是3位数颜色代码,请先转换为6位数版本。如果alpha值与6位十六进制一起提供,则返回`rgba()`字符串。"
	},
	"hide": {
		"prefix": "30s_hide",
		"body": [
			"const hide = (...el) => [...el].forEach(e => (e.style.display = 'none'));"
		],
		"description": "隐藏指定的所有元素。 使用`NodeList.prototype.forEach()`将`display:none`应用于指定的每个元素。"
	},
	"httpGet": {
		"prefix": "30s_httpGet",
		"body": [
			"const httpGet = (url, callback, err = console.error) => {",
			"  const request = new XMLHttpRequest();",
			"  request.open('GET', url, true);",
			"  request.onload = () => callback(request.responseText);",
			"  request.onerror = () => err(request);",
			"  request.send();",
			"};"
		],
		"description": "对传递的URL发出`GET`请求。 使用[`XMLHttpRequest`](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest)web api制作一个`get`请求给定的`url`。处理`onload`事件,通过调用给定的`callback``responseText`。通过运行提供的`err`函数来处理`onerror`事件。Omit第三个参数,`err`,默认情况下将错误记录到控制台的`error`流中。"
	},
	"httpPost": {
		"prefix": "30s_httpPost",
		"body": [
			"const httpPost = (url, data, callback, err = console.error) => {",
			"  const request = new XMLHttpRequest();",
			"  request.open('POST', url, true);",
			"  request.setRequestHeader('Content-type', 'application/json; charset=utf-8');",
			"  request.onload = () => callback(request.responseText);",
			"  request.onerror = () => err(request);",
			"  request.send(data);",
			"};"
		],
		"description": "对传递的URL发出`POST`请求​​。 使用[`XMLHttpRequest`](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest)web api制作一个`post`请求给定的`url`。使用`setRequestHeader`方法设置`HTTP`请求头的值。通过调用给定的`callback``responseText`来处理`onload`事件。Handle `onerror`事件,通过运行提供的`err`函数。省略第三个参数`data`,不向提供的`url`发送数据。省略第四个参数`err`,将错误记录到默认情况下,控制台的“错误”流。"
	},
	"httpsRedirect": {
		"prefix": "30s_httpsRedirect",
		"body": [
			"const httpsRedirect = () => {",
			"  if (location.protocol !== 'https:') location.replace('https://' + location.href.split('//')[1]);",
			"};"
		],
		"description": "如果页面当前处于HTTP状态,则将页面重定向到HTTPS。此外,按下后退按钮不会将其恢复到HTTP页面,因为它在历史记录中被替换。 使用`location.protocol`来获取当前正在使用的协议。如果它不是HTTPS,请使用`location.replace()`将现有页面替换为页面的HTTPS版本。使用`location.href`获取完整地址,用`String.prototype.split()`拆分它,并删除URL的协议部分。"
	},
	"hz": {
		"prefix": "30s_hz",
		"body": [
			"const hz = (fn, iterations = 100) => {",
			"  const before = performance.now();",
			"  for (let i = 0; i < iterations; i++) fn();",
			"  return (1000 * iterations) / (performance.now() - before);",
			"};"
		],
		"description": "返回每秒执行函数的次数。 `hz`是`hertz`的单位,频率单位定义为每秒一个周期。 使用`performance.now()`来获取迭代循环之前和之后的毫秒差值来计算执行函数`iterations`次所用的时间。 通过将毫秒转换为秒并将其除以经过的时间来返回每秒的周期数。 省略第二个参数`iterations`,使用默认的100次迭代。"
	},
	"indentString": {
		"prefix": "30s_indentString",
		"body": [
			"const indentString = (str, count, indent = ' ') => str.replace(/^/gm, indent.repeat(count));"
		],
		"description": "缩进提供的字符串中的每一行。 使用`String.replace`和一个正则表达式,在每行的开头添加由'indent``count`次指定的字符。省略第三个参数`indent` ,使用'''`的默认缩进字符。"
	},
	"indexOfAll": {
		"prefix": "30s_indexOfAll",
		"body": [
			"const indexOfAll = (arr, val) => arr.reduce((acc, el, i) => (el === val ? [...acc, i] : acc), []);"
		],
		"description": "返回数组中`val`的所有索引。如果`val`永远不会发生,返回`[]`。 使用`Array.prototype.reduce()`循环元素并存储匹配元素的索引。 n返回索引数组。"
	},
	"initial": {
		"prefix": "30s_initial",
		"body": [
			"const initial = arr => arr.slice(0, -1);"
		],
		"description": "返回除最后一个数组之外的数组的所有元素。 使用`arr.slice(0,-1)`返回除数组的最后一个元素之外的所有元素。"
	},
	"initialize2DArray": {
		"prefix": "30s_initialize2DArray",
		"body": [
			"const initialize2DArray = (w, h, val = null) =>",
			"  Array.from({ length: h }).map(() => Array.from({ length: w }).fill(val));"
		],
		"description": "初始化给定宽度和高度和值的2D数组。 使用`Array.prototype.map()`生成h行,其中每个都是一个大小为w的新数组,初始化为value。如果未提供该值,则默认为“null”。"
	},
	"initializeArrayWithRange": {
		"prefix": "30s_initializeArrayWithRange",
		"body": [
			"const initializeArrayWithRange = (end, start = 0, step = 1) =>",
			"  Array.from({ length: Math.ceil((end - start + 1) / step) }, (v, i) => i * step + start);"
		],
		"description": "初始化一个数组,其中包含指定范围内的数字,其中`start`和`end`包含它们的公共差值`step`。 使用`Array.from()`创建一个所需长度的数组,`( end  - start + 1)/step`,以及一个map函数,用给定范围内的所需值填充它。你可以省略`start`使用默认值`0`。你可以省略`step`使用默认值“1”。"
	},
	"initializeArrayWithRangeRight": {
		"prefix": "30s_initializeArrayWithRangeRight",
		"body": [
			"const initializeArrayWithRangeRight = (end, start = 0, step = 1) =>",
			"  Array.from({ length: Math.ceil((end + 1 - start) / step) }).map(",
			"    (v, i, arr) => (arr.length - i - 1) * step + start",
			"  );"
		],
		"description": "初始化一个数组,其中包含指定范围内的数字(反向),其中`start`和`end`包含它们的公共差值`step`。 使用`Array.from(Math.ceil((end + 1-)开始)/步))`创建一个所需长度的数组(元素的数量等于`(end-start)/step`或`(end + 1-start)/step` for inclusive end),` Array.prototype.map()`用于填充范围内的所需值。您可以省略`start`以使用默认值`0`。您可以省略`step`以使用默认值`1 `。"
	},
	"initializeArrayWithValues": {
		"prefix": "30s_initializeArrayWithValues",
		"body": [
			"const initializeArrayWithValues = (n, val = 0) => Array(n).fill(val);"
		],
		"description": "使用指定的值初始化并填充数组。 使用`Array(n)`创建一个所需长度的数组,`fill(v)`用它填充所需的值。你可以省略`val`使用默认值“0”。"
	},
	"initializeNDArray": {
		"prefix": "30s_initializeNDArray",
		"body": [
			"const initializeNDArray = (val, ...args) =>",
			"  args.length === 0",
			"    ? val",
			"    : Array.from({ length: args[0] }).map(() => initializeNDArray(val, ...args.slice(1)));"
		],
		"description": "创建一个给定值的n维数组。 使用递归。使用`Array.prototype.map()`生成行,其中每个都是使用`initializeNDArray`初始化的新数组。"
	},
	"inRange": {
		"prefix": "30s_inRange",
		"body": [
			"const inRange = (n, start, end = null) => {",
			"  if (end && start > end) [end, start] = [start, end];",
			"  return end == null ? n >= 0 && n < start : n >= start && n < end;",
			"};"
		],
		"description": "检查给定数字是否在给定范围内。 使用算术比较来检查给定数字是否在指定范围内。如果未指定第二个参数`end`,则认为范围来自`0`到`start`。"
	},
	"insertAfter": {
		"prefix": "30s_insertAfter",
		"body": [
			"const insertAfter = (el, htmlString) => el.insertAdjacentHTML('afterend', htmlString);"
		],
		"description": "在指定元素结束后插入一个HTML字符串。 使用`'afterend'的位置`el.insertAdjacentHTML()`来解析`htmlString`并在`el`结束后插入它。"
	},
	"insertBefore": {
		"prefix": "30s_insertBefore",
		"body": [
			"const insertBefore = (el, htmlString) => el.insertAdjacentHTML('beforebegin', htmlString);"
		],
		"description": "在指定元素的开头之前插入一个HTML字符串。 使用`el.insertAdjacentHTML()`,其位置为''beforebegin'`来解析`htmlString`并在`el`开头之前插入它。"
	},
	"intersection": {
		"prefix": "30s_intersection",
		"body": [
			"const intersection = (a, b) => {",
			"  const s = new Set(b);",
			"  return a.filter(x => s.has(x));",
			"};"
		],
		"description": "返回两个数组中存在的元素列表。 从`b`创建`Set`,然后在`a`上使用`Array.prototype.filter()`只保留`b`中包含的值。 ñ"
	},
	"intersectionBy": {
		"prefix": "30s_intersectionBy",
		"body": [
			"const intersectionBy = (a, b, fn) => {",
			"  const s = new Set(b.map(fn));",
			"  return a.filter(x => s.has(fn(x)));",
			"};"
		],
		"description": "在将提供的函数应用于两者的每个数组元素之后,返回两个数组中存在的元素列表。 通过将`fn`应用于`b`中的所有元素来创建`Set`,然后使用`Array.prototype .filter()`on`a`只保留元素,当`fn`应用于它们时,它们产生`b`中包含的值。"
	},
	"intersectionWith": {
		"prefix": "30s_intersectionWith",
		"body": [
			"const intersectionWith = (a, b, comp) => a.filter(x => b.findIndex(y => comp(x, y)) !== -1);"
		],
		"description": "使用提供的比较器函数返回两个数组中存在的元素列表。 使用`Array.prototype.filter()`和`Array.prototype.findIndex()`结合提供的比较器来确定交叉值。"
	},
	"invertKeyValues": {
		"prefix": "30s_invertKeyValues",
		"body": [
			"const invertKeyValues = (obj, fn) =>",
			"  Object.keys(obj).reduce((acc, key) => {",
			"    const val = fn ? fn(obj[key]) : obj[key];",
			"    acc[val] = acc[val] || [];",
			"    acc[val].push(key);",
			"    return acc;",
			"  }, {});"
		],
		"description": "反转对象的键值对,而不改变它。每个反转键的相应反转值是负责产生反转值的键阵列。如果提供了一个函数,它将应用于每个反转的键。 使用`Object.keys()`和`Array.prototype.reduce()`来反转对象的键值对并应用提供的函数(如果有的话)。省略第二个参数`fn`,得到反转键而不向它们应用函数。"
	},
	"is": {
		"prefix": "30s_is",
		"body": [
			"const is = (type, val) => ![, null].includes(val) && val.constructor === type;"
		],
		"description": "检查提供的值是否为指定类型。 使用`Array.prototype.includes()`确保值不是`undefined`或`null`,并将值的`constructor`属性与`type进行比较`检查提供的值是否是指定的`type`。"
	},
	"isAbsoluteURL": {
		"prefix": "30s_isAbsoluteURL",
		"body": [
			"const isAbsoluteURL = str => /^[a-z][a-z0-9+.-]*:/.test(str);"
		],
		"description": "如果给定的字符串是绝对URL,则返回“true”,否则返回“false”。 使用正则表达式来测试字符串是否为绝对URL。"
	},
	"isAfterDate": {
		"prefix": "30s_isAfterDate",
		"body": [
			"const isAfterDate = (dateA, dateB) => dateA > dateB;"
		],
		"description": "检查日期是否在另一个日期之后。 使用大于运算符(`>`)检查第一个日期是否在第二个日期之后。"
	},
	"isAnagram": {
		"prefix": "30s_isAnagram",
		"body": [
			"const isAnagram = (str1, str2) => {",
			"  const normalize = str =>",
			"    str",
			"      .toLowerCase()",
			"      .replace(/[^a-z0-9]/gi, '')",
			"      .split('')",
			"      .sort()",
			"      .join('');",
			"  return normalize(str1) === normalize(str2);",
			"};"
		],
		"description": "检查字符串是否是另一个字符串的字谜(不区分大小写,忽略空格,标点符号和特殊字符)。 使用带有适当正则表达式的`String.toLowerCase()`,`String.prototype.replace()`删除不必要的字符,`String.prototype.split('')`,`Array.prototype.sort()`和`Array.prototype.join('')`在两个字符串上规范化它们,然后检查它们是否归一化形式是平等的。"
	},
	"isArrayLike": {
		"prefix": "30s_isArrayLike",
		"body": [
			"const isArrayLike = obj => obj != null && typeof obj[Symbol.iterator] === 'function';"
		],
		"description": "检查提供的参数是否类似于数组(即可迭代)。 检查提供的参数是否为“null”且其“Symbol.iterator”属性是否为函数。"
	},
	"isBeforeDate": {
		"prefix": "30s_isBeforeDate",
		"body": [
			"const isBeforeDate = (dateA, dateB) => dateA < dateB;"
		],
		"description": "检查日期是否在另一个日期之前。 使用小于运算符(`<`)检查第一个日期是否在第二个日期之前。"
	},
	"isBoolean": {
		"prefix": "30s_isBoolean",
		"body": [
			"const isBoolean = val => typeof val === 'boolean';"
		],
		"description": "检查给定的参数是否是本机布尔元素。 使用`typeof`检查值是否被归类为布尔基元。"
	},
	"isBrowser": {
		"prefix": "30s_isBrowser",
		"body": [
			"const isBrowser = () => ![typeof window, typeof document].includes('undefined');"
		],
		"description": "确定当前运行时环境是否为浏览器,以便前端模块可以在服务器(节点)上运行而不会抛出错误。 在`window的`typeof`值上使用`Array.prototype.includes()` `和`document`(全局变量通常只在浏览器环境中可用,除非它们被明确定义),如果其中一个是'undefined`则返回`true`。`typeof`允许检查全局变量而不抛出一个`ReferenceError`。如果它们都不是'undefined`,那么假定当前环境是浏览器。"
	},
	"isBrowserTabFocused": {
		"prefix": "30s_isBrowserTabFocused",
		"body": [
			"const isBrowserTabFocused = () => !document.hidden;"
		],
		"description": "如果页面的浏览器选项卡是聚焦的,则返回“true”,否则返回“false”。 使用Page Visibility API引入的`Document.hidden`属性来检查页面的浏览器选项卡是否可见或隐藏。"
	},
	"isDivisible": {
		"prefix": "30s_isDivisible",
		"body": [
			"const isDivisible = (dividend, divisor) => dividend % divisor === 0;"
		],
		"description": "检查第一个数字参数是否可被第二个数字整除。 使用模运算符(`%`)检查余数是否等于'0`。"
	},
	"isDuplexStream": {
		"prefix": "30s_isDuplexStream",
		"body": [
			"const isDuplexStream = val =>",
			"  val !== null &&",
			"  typeof val === 'object' &&",
			"  typeof val.pipe === 'function' &&",
			"  typeof val._read === 'function' &&",
			"  typeof val._readableState === 'object' &&",
			"  typeof val._write === 'function' &&",
			"  typeof val._writableState === 'object';"
		],
		"description": "检查给定的参数是否是双工(可读写)流。 检查该值是否与`null`不同,使用`typeof`检查值是否为`object`和`pipe`属性类型为`function`。另外检查`typeof``_read`,`_ write`和`_readableState`,`_ writeableState`属性是否分别是`function`和`object`。"
	},
	"isEmpty": {
		"prefix": "30s_isEmpty",
		"body": [
			"const isEmpty = val => val == null || !(Object.keys(val) || val).length;"
		],
		"description": "如果a值是空对象,集合,没有可枚举属性或任何不被视为集合的类型,则返回true。 检查提供的值是否为“null”或者其“length”是否等于` 0`。"
	},
	"isEven": {
		"prefix": "30s_isEven",
		"body": [
			"const isEven = num => num % 2 === 0;"
		],
		"description": "如果给定的数字是偶数,则返回“true”,否则返回“false”。 使用模数(`%`)运算符检查数字是否为奇数。如果数字为偶数,则返回“true”,“false” `如果数字是奇数。"
	},
	"isFunction": {
		"prefix": "30s_isFunction",
		"body": [
			"const isFunction = val => typeof val === 'function';"
		],
		"description": "检查给定的参数是否为函数。 使用`typeof`检查值是否被归类为函数原语。"
	},
	"isLowerCase": {
		"prefix": "30s_isLowerCase",
		"body": [
			"const isLowerCase = str => str === str.toLowerCase();"
		],
		"description": "检查字符串是否为小写。 将给定的字符串转换为小写,使用`String.toLowerCase()`并将其与原始字符串进行比较。"
	},
	"isNegativeZero": {
		"prefix": "30s_isNegativeZero",
		"body": [
			"const isNegativeZero = val => val === 0 && 1 / val === -Infinity;"
		],
		"description": "检查给定值是否等于负零(`-0`)。 检查传递的值是否等于“0”,如果“1”除以值等于“-Infinity”。"
	},
	"isNil": {
		"prefix": "30s_isNil",
		"body": [
			"const isNil = val => val === undefined || val === null;"
		],
		"description": "如果指定的值为“null”或“undefined”,则返回“true”,否则返回“false”。 使用严格相等运算符检查值和“val”是否等于“null”或“undefined” 。"
	},
	"isNull": {
		"prefix": "30s_isNull",
		"body": [
			"const isNull = val => val === null;"
		],
		"description": "如果指定的值为“null”则返回“true”,否则返回“false”。 使用严格相等运算符检查值和“val”是否等于“null”。"
	},
	"isNumber": {
		"prefix": "30s_isNumber",
		"body": [
			"const isNumber = val => typeof val === 'number' && val === val;"
		],
		"description": "检查给定的参数是否为数字。 使用`typeof`检查值是否被归类为数字原语。 要防止`NaN`,检查`val === val`(因为`NaN`的`typeof`等于`number`并且唯一的值不等于它自己)。"
	},
	"isObject": {
		"prefix": "30s_isObject",
		"body": [
			"const isObject = obj => obj === Object(obj);"
		],
		"description": "返回一个布尔值,确定传递的值是否为对象。 使用`Object`构造函数为给定值创建对象包装器。 如果值为“null”或“undefined”,则创建并返回一个空对象。否则,返回与给定值对应的类型的对象。"
	},
	"isObjectLike": {
		"prefix": "30s_isObjectLike",
		"body": [
			"const isObjectLike = val => val !== null && typeof val === 'object';"
		],
		"description": "检查值是否类似于对象。 检查提供的值是否为“null”且其“typeof”是否等于“object”。"
	},
	"isPlainObject": {
		"prefix": "30s_isPlainObject",
		"body": [
			"const isPlainObject = val => !!val && typeof val === 'object' && val.constructor === Object;"
		],
		"description": "检查提供的值是否是Object构造函数创建的对象。 检查提供的值是否真实,使用`typeof`检查它是否是对象,使用`Object.constructor`确保构造函数等于`Object`。"
	},
	"isPrime": {
		"prefix": "30s_isPrime",
		"body": [
			"const isPrime = num => {",
			"  const boundary = Math.floor(Math.sqrt(num));",
			"  for (var i = 2; i <= boundary; i++) if (num % i === 0) return false;",
			"  return num >= 2;",
			"};"
		],
		"description": "检查提供的整数是否为素数。 检查从“2”到给定数字的平方根的数字。如果其中任何一个除以给定的数字,则返回“false”,否则返回“true”,除非数字小于“2”。"
	},
	"isPrimitive": {
		"prefix": "30s_isPrimitive",
		"body": [
			"const isPrimitive = val => Object(val) !== val;"
		],
		"description": "返回一个布尔值,确定传递的值是否为原始值。 从`val`创建一个对象并将其与`val`进行比较,以确定传递的值是否为原始值(即不等于创建的对象)。"
	},
	"isPromiseLike": {
		"prefix": "30s_isPromiseLike",
		"body": [
			"const isPromiseLike = obj =>",
			"  obj !== null &&",
			"  (typeof obj === 'object' || typeof obj === 'function') &&",
			"  typeof obj.then === 'function';"
		],
		"description": "如果对象看起来像[`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise),则返回`true`,否则返回`false`。 检查对象是否为“null”,它的`typeof`匹配`object`或`function`,如果它有`.then`属性,它也是`function`。"
	},
	"isReadableStream": {
		"prefix": "30s_isReadableStream",
		"body": [
			"const isReadableStream = val =>",
			"  val !== null &&",
			"  typeof val === 'object' &&",
			"  typeof val.pipe === 'function' &&",
			"  typeof val._read === 'function' &&",
			"  typeof val._readableState === 'object';"
		],
		"description": "检查给定参数是否是可读流。 检查值是否与`null`不同,使用`typeof`检查值是否为`object`类型,`pipe`属性是否为`function`类型`。另外检查`typeof``_read`和`_readableState`属性是否分别是`function`和`object`。"
	},
	"isSameDate": {
		"prefix": "30s_isSameDate",
		"body": [
			"const isSameDate = (dateA, dateB) => dateA.toISOString() === dateB.toISOString();"
		],
		"description": "检查日期是否与另一个日期相同。 使用`Date.prototype.toISOString()`和严格相等检查(`===`)来检查第一个日期是否与第二个日期相同。 ñ"
	},
	"isSorted": {
		"prefix": "30s_isSorted",
		"body": [
			"const isSorted = arr => {",
			"  let direction = -(arr[0] - arr[1]);",
			"  for (let [i, val] of arr.entries()) {",
			"    direction = !direction ? -(arr[i - 1] - arr[i]) : direction;",
			"    if (i === arr.length - 1) return !direction ? 0 : direction;",
			"    else if ((val - arr[i + 1]) * direction > 0) return 0;",
			"  }",
			"};"
		],
		"description": "如果数组按升序排序则返回“1”,如果按降序排序则返回“-1”,如果未按顺序排序则返回“0”。 计算前两个元素的排序“方向”。 n使用`Object.entries()`循环遍历数组对象并成对比较它们。如果`direction`改变则返回'0`或如果到达最后一个元素则返回`direction`。"
	},
	"isStream": {
		"prefix": "30s_isStream",
		"body": [
			"const isStream = val => val !== null && typeof val === 'object' && typeof val.pipe === 'function';"
		],
		"description": "检查给定的参数是否是流。 检查值是否与`null`不同,使用`typeof`检查值是否为`object`类型,`pipe`属性是否为`function`类型。"
	},
	"isString": {
		"prefix": "30s_isString",
		"body": [
			"const isString = val => typeof val === 'string';"
		],
		"description": "检查给定的参数是否为字符串。仅适用于字符串基元。 使用`typeof`检查值是否归类为字符串基元。"
	},
	"isSymbol": {
		"prefix": "30s_isSymbol",
		"body": [
			"const isSymbol = val => typeof val === 'symbol';"
		],
		"description": "检查给定的参数是否为符号。 使用`typeof`检查值是否被归类为符号原语。"
	},
	"isTravisCI": {
		"prefix": "30s_isTravisCI",
		"body": [
			"const isTravisCI = () => 'TRAVIS' in process.env && 'CI' in process.env;"
		],
		"description": "检查当前环境是否为[Travis CI](https://travis-ci.org/)。 检查当前环境是否具有`TRAVIS`和`CI`环境变量([reference](https://docs.travis-ci.com/user/environment-variables/#Default-Environment-Variables)).\n"
	},
	"isUndefined": {
		"prefix": "30s_isUndefined",
		"body": [
			"const isUndefined = val => val === undefined;"
		],
		"description": "如果指定的值是'undefined`则返回`true`,否则返回`false`。 使用strict equality运算符检查值和'val`是否等于`undefined`。"
	},
	"isUpperCase": {
		"prefix": "30s_isUpperCase",
		"body": [
			"const isUpperCase = str => str === str.toUpperCase();"
		],
		"description": "检查字符串是否为大写。 使用`String.prototype.toUpperCase()将给定的字符串转换为大写,并将其与原始字符串进行比较。"
	},
	"isValidJSON": {
		"prefix": "30s_isValidJSON",
		"body": [
			"const isValidJSON = str => {",
			"  try {",
			"    JSON.parse(str);",
			"    return true;",
			"  } catch (e) {",
			"    return false;",
			"  }",
			"};"
		],
		"description": "检查提供的字符串是否是有效的JSON。 使用`JSON.parse()`和`try ... catch`块来检查提供的字符串是否是有效的JSON。"
	},
	"isWeekday": {
		"prefix": "30s_isWeekday",
		"body": [
			"const isWeekday = (t = new Date()) => {",
			"  return t.getDay() % 6 !== 0;",
			"};"
		],
		"description": "导致特定日期的布尔表示。 首先使用特定日期对象。使用`Date.getDay()`通过使用模运算符检查工作日,然后返回布尔值。"
	},
	"isWeekend": {
		"prefix": "30s_isWeekend",
		"body": [
			"const isWeekend = (t = new Date()) => {",
			"  return t.getDay() % 6 === 0;",
			"};"
		],
		"description": "导致特定日期的布尔表示。 首先使用特定日期对象。使用`Date.getDay()`根据返回的日期使用模运算检查周末为0  - 6然后返回布尔值。"
	},
	"isWritableStream": {
		"prefix": "30s_isWritableStream",
		"body": [
			"const isWritableStream = val =>",
			"  val !== null &&",
			"  typeof val === 'object' &&",
			"  typeof val.pipe === 'function' &&",
			"  typeof val._write === 'function' &&",
			"  typeof val._writableState === 'object';"
		],
		"description": "检查给定的参数是否是可写流。 检查该值是否与`null`不同,使用`typeof`检查值是否为`object`类型,`pipe`属性是否为`function`类型`。另外检查`typeof``__write`和`_writableState`属性是否分别是`function`和`object`。"
	},
	"join": {
		"prefix": "30s_join",
		"body": [
			"const join = (arr, separator = ',', end = separator) =>",
			"  arr.reduce(",
			"    (acc, val, i) =>",
			"      i === arr.length - 2",
			"        ? acc + val + end",
			"        : i === arr.length - 1",
			"        ? acc + val",
			"        : acc + val + separator,",
			"    ''",
			"  );"
		],
		"description": "将数组的所有元素连接成一个字符串并返回该字符串。使用分隔符和结束分隔符。 使用`Array.prototype.reduce()`将元素组合成一个字符串。省略第二个参数,` separator`,使用`','`的默认分隔符。省略第三个参数`end`,默认情况下使用与`separator`相同的值。"
	},
	"JSONtoCSV": {
		"prefix": "30s_JSONtoCSV",
		"body": [
			"const JSONtoCSV = (arr, columns, delimiter = ',') =>",
			"  [",
			"    columns.join(delimiter),",
			"    ...arr.map(obj =>",
			"      columns.reduce(",
			"        (acc, key) => `${acc}${!acc.length ? '' : delimiter}\"${!obj[key] ? '' : obj[key]}\"`,",
			"        ''",
			"      )",
			"    )",
			"  ].join('\\n');"
		],
		"description": "将一个对象数组转换为逗号分隔值(CSV)字符串,该字符串仅包含指定的`columns`。 使用`Array.prototype.join(delimiter)`组合`columns`中的所有名称以创建第一行。使用`Array.prototype.map()`和`Array.prototype.reduce()`为每个对象创建一行,用空字符串替换不存在的值,只在`columns`中映射值。 n使用`Array.prototype.join('')`将所有行组合成一个字符串。省略第三个参数`delimiter`,使用`,`的默认分隔符。"
	},
	"JSONToFile": {
		"prefix": "30s_JSONToFile",
		"body": [
			"const fs = require('fs');",
			"const JSONToFile = (obj, filename) =>",
			"  fs.writeFile(`${filename}.json`, JSON.stringify(obj, null, 2));"
		],
		"description": "将JSON对象写入文件。 使用`fs.writeFile()`,模板文字和`JSON.stringify()`将`json`对象写入`.json`文件。"
	},
	"last": {
		"prefix": "30s_last",
		"body": [
			"const last = arr => arr[arr.length - 1];"
		],
		"description": "返回数组中的最后一个元素。 使用`arr.length  - 1`来计算给定数组的最后一个元素的索引并返回它。"
	},
	"lcm": {
		"prefix": "30s_lcm",
		"body": [
			"const lcm = (...arr) => {",
			"  const gcd = (x, y) => (!y ? x : gcd(y, x % y));",
			"  const _lcm = (x, y) => (x * y) / gcd(x, y);",
			"  return [...arr].reduce((a, b) => _lcm(a, b));",
			"};"
		],
		"description": "返回两个或多个数字的最小公倍数。 使用最大公约数(GCD)公式和`lcm(x,y)= x *y /gcd(x,y)`以确定最小公数常用倍数。 GCD公式使用递归。"
	},
	"longestItem": {
		"prefix": "30s_longestItem",
		"body": [
			"const longestItem = (...vals) => vals.reduce((a, x) => (x.length > a.length ? x : a));"
		],
		"description": "获取具有`length`属性的任意数量的可迭代对象或对象,并返回最长的属性。如果多个对象具有相同的长度,则将返回第一个。如果没有提供参数,则返回`undefined`。 使用`Array.prototype.reduce()`,比较对象的`length`以找到最长的对象。"
	},
	"lowercaseKeys": {
		"prefix": "30s_lowercaseKeys",
		"body": [
			"const lowercaseKeys = obj =>",
			"  Object.keys(obj).reduce((acc, key) => {",
			"    acc[key.toLowerCase()] = obj[key];",
			"    return acc;",
			"  }, {});"
		],
		"description": "从指定对象创建一个新对象,其中所有键都是小写的。 使用`Object.keys()`和`Array.prototype.reduce()`从指定对象创建一个新对象。Convert使用`String.toLowerCase()`。将原始对象中的每个键设置为小写"
	},
	"luhnCheck": {
		"prefix": "30s_luhnCheck",
		"body": [
			"const luhnCheck = num => {",
			"  let arr = (num + '')",
			"    .split('')",
			"    .reverse()",
			"    .map(x => parseInt(x));",
			"  let lastDigit = arr.splice(0, 1)[0];",
			"  let sum = arr.reduce((acc, val, i) => (i % 2 !== 0 ? acc + val : acc + ((val * 2) % 9) || 9), 0);",
			"  sum += lastDigit;",
			"  return sum % 10 === 0;",
			"};"
		],
		"description": "用于验证各种识别号码的[Luhn算法](https://en.wikipedia.org/wiki/Luhn_algorithm)的实现,例如信用卡号,IMEI号,国家提供商标识号等。 使用`String.prototype.split('')`,`Array.prototype.reverse()`和`Array.prototype.map()`结合`parseInt()`来获取数字数组。使用`Array .prototype.splice(0,1)`获取最后一位数。使用`Array.prototype.reduce()`来实现Luhn算法。如果`sum`可以被'10'整除,则返回'true`,`假的。否则。"
	},
	"mapKeys": {
		"prefix": "30s_mapKeys",
		"body": [
			"const mapKeys = (obj, fn) =>",
			"  Object.keys(obj).reduce((acc, k) => {",
			"    acc[fn(obj[k], k, obj)] = obj[k];",
			"    return acc;",
			"  }, {});"
		],
		"description": "创建一个对象,其中包含通过为每个键运行提供的函数生成的键以及与提供的对象相同的值。 使用`Object.keys(obj)`迭代对象的键。使用`Array.prototype.reduce ()`使用`fn`创建一个具有相同值和映射键的新对象。"
	},
	"mapNumRange": {
		"prefix": "30s_mapNumRange",
		"body": [
			"const mapNumRange = (num, inMin, inMax, outMin, outMax) =>",
			"  ((num - inMin) * (outMax - outMin)) / (inMax - inMin) + outMin;"
		],
		"description": "将一个数字从一个范围映射到另一个范围。 返回`num`映射到`inMin`-`inMax`中的`outMin` -`outMax`。"
	},
	"mapObject": {
		"prefix": "30s_mapObject",
		"body": [
			"const mapObject = (arr, fn) =>",
			"  (a => (",
			"    (a = [arr, arr.map(fn)]), a[0].reduce((acc, val, ind) => ((acc[val] = a[1][ind]), acc), {})",
			"  ))();"
		],
		"description": "使用函数将数组的值映射到对象,其中键 -值对由作为键的字符串化值和映射值组成。 使用匿名内部函数作用域来声明未定义的内存空间,使用闭包存储返回值。使用一个新的`Array`来存储数组,其中包含函数的数据集和一个逗号运算符,以返回第二步,而不需要从一个上下文移动到另一个上下文(由于闭包和操作顺序)。 ñ"
	},
	"mapString": {
		"prefix": "30s_mapString",
		"body": [
			"const mapString = (str, fn) =>",
			"  str",
			"    .split('')",
			"    .map((c, i) => fn(c, i, str))",
			"    .join('');"
		],
		"description": "创建一个新字符串,其结果是在调用字符串中的每个字符上调用一个提供的函数。 使用`String.prototype.split('')`和`Array.prototype.map()`来调用提供的函数,`fn`,用于`str`中的每个字符。使用`Array.prototype.join('')`将字符数组重新组合成一个字符串。回调函数`fn`接受三个参数(当前字符,当前字符的索引和字符串`mapString`被调用)。"
	},
	"mapValues": {
		"prefix": "30s_mapValues",
		"body": [
			"const mapValues = (obj, fn) =>",
			"  Object.keys(obj).reduce((acc, k) => {",
			"    acc[k] = fn(obj[k], k, obj);",
			"    return acc;",
			"  }, {});"
		],
		"description": "使用与提供的对象相同的键创建一个对象,并通过为每个值运行提供的函数生成值。 使用`Object.keys(obj)`迭代对象的键。使用`Array.prototype.reduce ()`使用`fn`创建一个具有相同键和映射值的新对象。"
	},
	"mask": {
		"prefix": "30s_mask",
		"body": [
			"const mask = (cc, num = 4, mask = '*') => `${cc}`.slice(-num).padStart(`${cc}`.length, mask);"
		],
		"description": "用指定的掩码字符替换除最后一个`num`个字符以外的所有字符。 使用`String.prototype.slice()`来获取将保持未屏蔽的字符部分,并使用`String.padStart()`用掩码字符填充字符串的开头直到原始长度。省略第二个参数`num`,以保持默认的`4`字符未被屏蔽。如果`num`为负数,则未屏蔽的字符将位于字符串的开头。省略第三个参数`mask`,为掩码使用默认字符''*'`。"
	},
	"matches": {
		"prefix": "30s_matches",
		"body": [
			"const matches = (obj, source) =>",
			"  Object.keys(source).every(key => obj.hasOwnProperty(key) && obj[key] === source[key]);"
		],
		"description": "比较两个对象以确定第一个对象是否包含与第二个对象相同的属性值。 使用`Object.keys(source)`来获取第二个对象的所有键,然后是`Array.prototype.every()` ,``Object.hasOwnProperty()`和严格比较,以确定第一个对象中是否存在所有键并具有相同的值。"
	},
	"matchesWith": {
		"prefix": "30s_matchesWith",
		"body": [
			"const matchesWith = (obj, source, fn) =>",
			"  Object.keys(source).every(key =>",
			"    obj.hasOwnProperty(key) && fn",
			"      ? fn(obj[key], source[key], key, obj, source)",
			"      : obj[key] == source[key]",
			"  );"
		],
		"description": "比较两个对象,以确定第一个对象是否包含与第二个对象相同的属性值,基于提供的函数。 使用`Object.keys(source)`获取第二个对象的所有键,然后是'Array。 prototype.every()`,`Object.hasOwnProperty()`以及提供的函数,用于确定第一个对象中是否存在所有键并具有等效值。如果未提供任何函数,则将使用相等运算符比较这些值。 "
	},
	"maxBy": {
		"prefix": "30s_maxBy",
		"body": [
			"const maxBy = (arr, fn) => Math.max(...arr.map(typeof fn === 'function' ? fn : val => val[fn]));"
		],
		"description": "使用提供的函数将每个元素映射到一个值后,返回数组的最大值。 使用`Array.prototype.map()`将每个元素映射到`fn`,`Math.max返回的值()`获取最大值。"
	},
	"maxDate": {
		"prefix": "30s_maxDate",
		"body": [
			"const maxDate = dates => new Date(Math.max(...dates));"
		],
		"description": "返回给定日期的最大值。 使用带有`Math.max`的ES6扩展语法来查找最大日期值,`new Date()`将其转换为`Date`对象。"
	},
	"maxN": {
		"prefix": "30s_maxN",
		"body": [
			"const maxN = (arr, n = 1) => [...arr].sort((a, b) => b - a).slice(0, n);"
		],
		"description": "返回提供的数组中的`n`最大元素。如果`n`大于或等于提供的数组的长度,则返回原始数组(按降序排序)。 使用`Array.prototype.sort ()`结合使用扩展运算符(`...`)来创建数组的浅层克隆并按降序对其进行排序。使用`Array.prototype.slice()`来获取指定数量的元素。 n省略第二个参数`n`,得到一个单元素数组。"
	},
	"median": {
		"prefix": "30s_median",
		"body": [
			"const median = arr => {",
			"  const mid = Math.floor(arr.length / 2),",
			"    nums = [...arr].sort((a, b) => a - b);",
			"  return arr.length % 2 !== 0 ? nums[mid] : (nums[mid - 1] + nums[mid]) / 2;",
			"};"
		],
		"description": "返回数组数组的中位数。 找到数组的中间部分,使用`Array.prototype.sort()`对值进行排序。如果`length`为奇数,则返回中间点的数字,否则返回两个中间数的平均值。"
	},
	"memoize": {
		"prefix": "30s_memoize",
		"body": [
			"const memoize = fn => {",
			"  const cache = new Map();",
			"  const cached = function(val) {",
			"    return cache.has(val) ? cache.get(val) : cache.set(val, fn.call(this, val)) && cache.get(val);",
			"  };",
			"  cached.cache = cache;",
			"  return cached;",
			"};"
		],
		"description": "返回memoized(缓存)函数。 通过实例化一个新的`Map`对象来创建一个空缓存。返回一个函数,它通过首先检查函数的输出是否为该特定函数提供给memoized函数的单个参数。输入值已经缓存,或者存储并返回它,如果没有。必须使用`function`关键字,以便允许memoized函数在必要时更改其`this`上下文。通过将其设置为返回函数的属性来允许访问`cache`。"
	},
	"merge": {
		"prefix": "30s_merge",
		"body": [
			"const merge = (...objs) =>",
			"  [...objs].reduce(",
			"    (acc, obj) =>",
			"      Object.keys(obj).reduce((a, k) => {",
			"        acc[k] = acc.hasOwnProperty(k) ? [].concat(acc[k]).concat(obj[k]) : obj[k];",
			"        return acc;",
			"      }, {}),",
			"    {}",
			"  );"
		],
		"description": "从两个或多个对象的组合创建一个新对象。 使用`Array.prototype.reduce()`结合`Object.keys(obj)`迭代所有对象和键。使用`hasOwnProperty() `和`Array.prototype.concat()`为多个对象中存在的键附加值。"
	},
	"midpoint": {
		"prefix": "30s_midpoint",
		"body": [
			"const midpoint = ([x1, y1], [x2, y2]) => [(x1 + x2) / 2, (y1 + y2) / 2];"
		],
		"description": "计算两对(x,y)点之间的中点。 构造数组以获得`x1`,`y1`,`x2`和`y2`,通过除以总和来计算每个维度的中点两个端点由`2`。"
	},
	"minBy": {
		"prefix": "30s_minBy",
		"body": [
			"const minBy = (arr, fn) => Math.min(...arr.map(typeof fn === 'function' ? fn : val => val[fn]));"
		],
		"description": "使用提供的函数将每个元素映射到一个值后,返回数组的最小值。 使用`Array.prototype.map()`将每个元素映射到`fn`,`Math.min返回的值()`获得最小值。"
	},
	"minDate": {
		"prefix": "30s_minDate",
		"body": [
			"const minDate = dates => new Date(Math.min(...dates));"
		],
		"description": "返回给定日期的最小值。 使用ES6扩展语法查找最小日期值,`new Date()`将其转换为`Date`对象。"
	},
	"minN": {
		"prefix": "30s_minN",
		"body": [
			"const minN = (arr, n = 1) => [...arr].sort((a, b) => a - b).slice(0, n);"
		],
		"description": "返回提供的数组中的`n`最小元素。如果`n`大于或等于提供的数组的长度,则返回原始数组(按升序排序)。 使用`Array.prototype.sort ()`结合使用扩展运算符(`...`)创建数组的浅层克隆并按升序排序。使用`Array.prototype.slice()`获取指定数量的元素。 n省略第二个参数`n`,得到一个单元素数组。"
	},
	"mostPerformant": {
		"prefix": "30s_mostPerformant",
		"body": [
			"const mostPerformant = (fns, iterations = 10000) => {",
			"  const times = fns.map(fn => {",
			"    const before = performance.now();",
			"    for (let i = 0; i < iterations; i++) fn();",
			"    return performance.now() - before;",
			"  });",
			"  return times.indexOf(Math.min(...times));",
			"};"
		],
		"description": "返回执行最快的函数数组中函数的索引。 使用`Array.prototype.map()`生成一个数组,其中每个值是在`iterations`次之后执行函数所用的总时间。使用之前和之后的`performance.now()`值的差异来获得高精度的总时间(以毫秒为单位)。使用`Math.min()`找到最小执行时间,并返回索引最短的时间,它对应于性能最佳的函数的索引。省略第二个参数`iterations`,使用默认的10,000次迭代。迭代次数越多,结果越可靠,但需要的时间越长。"
	},
	"negate": {
		"prefix": "30s_negate",
		"body": [
			"const negate = func => (...args) => !func(...args);"
		],
		"description": "否定谓词函数。 取一个谓词函数并使用其参数对其应用not运算符(`!`)。"
	},
	"nest": {
		"prefix": "30s_nest",
		"body": [
			"const nest = (items, id = null, link = 'parent_id') =>",
			"  items",
			"    .filter(item => item[link] === id)",
			"    .map(item => ({ ...item, children: nest(items, item.id) }));"
		],
		"description": "给定一个彼此链接的平面对象数组,它将递归地嵌套它们。有用于嵌套注释,例如reddit.com上的注释。 使用递归。使用`Array.prototype.filter()`过滤`id`与`link`匹配的项目,然后`Array.prototype.map()`将每个项目映射到一个新的对象,该对象具有`children`属性,根据哪些是子项递归嵌套项目当前项目。省略第二个参数`id`,默认为'null`,表示对象没有链接到另一个(即它是顶级对象)。省略第三个参数,`link` ,使用`'parent_id'`作为默认属性,通过`id`将对象链接到另一个对象。"
	},
	"nodeListToArray": {
		"prefix": "30s_nodeListToArray",
		"body": [
			"const nodeListToArray = nodeList => [...nodeList];"
		],
		"description": "将`NodeList`转换为数组。 在新数组中使用扩展运算符将`NodeList`转换为数组。"
	},
	"none": {
		"prefix": "30s_none",
		"body": [
			"const none = (arr, fn = Boolean) => !arr.some(fn);"
		],
		"description": "如果提供的谓词函数为集合中的所有元素返回“false”,则返回“true”,否则返回“false”。 使用`Array.prototype.some()`来测试集合中的任何元素是否返回“true”基于`fn`。省略第二个参数`fn`,使用`Boolean`作为默认值。"
	},
	"nthArg": {
		"prefix": "30s_nthArg",
		"body": [
			"const nthArg = n => (...args) => args.slice(n)[0];"
		],
		"description": "创建一个在索引“n”处获取参数的函数。如果`n`为负数,则返回结尾的第n个参数。 使用`Array.prototype.slice()`在索引`n`获取所需的参数。"
	},
	"nthElement": {
		"prefix": "30s_nthElement",
		"body": [
			"const nthElement = (arr, n = 0) => (n === -1 ? arr.slice(n) : arr.slice(n, n + 1))[0];"
		],
		"description": "返回数组的第n个元素。 使用`Array.prototype.slice()`来获取包含第一个元素的数组。如果索引超出范围,则返回`undefined`。Omit获取数组的第一个元素的第二个参数`n`。"
	},
	"objectFromPairs": {
		"prefix": "30s_objectFromPairs",
		"body": [
			"const objectFromPairs = arr => arr.reduce((a, [key, val]) => ((a[key] = val), a), {});"
		],
		"description": "从给定的键值对创建一个对象。 使用`Array.prototype.reduce()`来创建和组合键值对。"
	},
	"objectToPairs": {
		"prefix": "30s_objectToPairs",
		"body": [
			"const objectToPairs = obj => Object.keys(obj).map(k => [k, obj[k]]);"
		],
		"description": "从对象创建一个键值对数组数组。 使用`Object.keys()`和`Array.prototype.map()`迭代对象的键并生成一个带键值对的数组。 "
	},
	"observeMutations": {
		"prefix": "30s_observeMutations",
		"body": [
			"const observeMutations = (element, callback, options) => {",
			"  const observer = new MutationObserver(mutations => mutations.forEach(m => callback(m)));",
			"  observer.observe(",
			"    element,",
			"    Object.assign(",
			"      {",
			"        childList: true,",
			"        attributes: true,",
			"        attributeOldValue: true,",
			"        characterData: true,",
			"        characterDataOldValue: true,",
			"        subtree: true",
			"      },",
			"      options",
			"    )",
			"  );",
			"  return observer;",
			"};"
		],
		"description": "返回一个新的MutationObserver并为指定元素上的每个变异运行提供的回调。 使用[`MutationObserver`](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver)观察给定元素的突变。使用`Array.prototype.forEach()`为每个观察到的突变运行回调。省略第三个参数`options`,使用默认的[options](https: //developer.mozilla.org/en-US/docs/Web/API/MutationObserver#MutationObserverInit)(全部为'true`)。"
	},
	"off": {
		"prefix": "30s_off",
		"body": [
			"const off = (el, evt, fn, opts = false) => el.removeEventListener(evt, fn, opts);"
		],
		"description": "从元素中删除事件侦听器。 使用`EventTarget.removeEventListener()`从元素中删除事件侦听器。 允许第四个参数`opts`使用`false`或根据添加事件监听器时使用的选项指定它。"
	},
	"offset": {
		"prefix": "30s_offset",
		"body": [
			"const offset = (arr, offset) => [...arr.slice(offset), ...arr.slice(0, offset)];"
		],
		"description": "将指定数量的元素移动到数组的末尾。 使用`Array.prototype.slice()`两次以获取指定索引之后的元素和之前的元素。使用扩展运算符(`.. .`)将两者合并为一个数组。如果`offset`为负数,则元素将从一端移动到另一端。"
	},
	"omit": {
		"prefix": "30s_omit",
		"body": [
			"const omit = (obj, arr) =>",
			"  Object.keys(obj)",
			"    .filter(k => !arr.includes(k))",
			"    .reduce((acc, key) => ((acc[key] = obj[key]), acc), {});"
		],
		"description": "从对象中省略与给定键对应的键值对。 使用`Object.keys(obj)`,`Array.prototype.filter()`和`Array.prototype.includes()`删除提供键。使用`Array.prototype.reduce()`将过滤后的键转换回具有相应键值对的对象。"
	},
	"omitBy": {
		"prefix": "30s_omitBy",
		"body": [
			"const omitBy = (obj, fn) =>",
			"  Object.keys(obj)",
			"    .filter(k => !fn(obj[k], k))",
			"    .reduce((acc, key) => ((acc[key] = obj[key]), acc), {});"
		],
		"description": "创建一个由给定函数返回falsy的属性组成的对象。该函数使用两个参数调用:(value,key)。 使用`Object.keys(obj)`和`Array.prototype.filter()`来删除`fn`返回truthy值的键。 使用`Array.prototype.reduce()`将过滤后的密钥转换回具有相应键值对的对象。"
	},
	"on": {
		"prefix": "30s_on",
		"body": [
			"const on = (el, evt, fn, opts = {}) => {",
			"  const delegatorFn = e => e.target.matches(opts.target) && fn.call(e.target, e);",
			"  el.addEventListener(evt, opts.target ? delegatorFn : fn, opts.options || false);",
			"  if (opts.target) return delegatorFn;",
			"};"
		],
		"description": "向具有使用事件委托功能的元素添加事件侦听器。 使用`EventTarget.addEventListener()`向元素添加事件侦听器。如果有一个`target`属性提供给options对象,请确保事件目标与指定的目标匹配,然后通过提供正确的`this`上下文来调用回调。返回对自定义委托者函数的引用,以便可以与[`off`](#off)一起使用。'将'opts`默认为非委托行为和事件冒泡。"
	},
	"once": {
		"prefix": "30s_once",
		"body": [
			"const once = fn => {",
			"  let called = false;",
			"  return function(...args) {",
			"    if (called) return;",
			"    called = true;",
			"    return fn.apply(this, args);",
			"  };",
			"};"
		],
		"description": "确保只调用一次函数。 使用一个闭包,使用一个标志``called`,并在第一次调用该函数时将其设置为`true`,防止再次调用它。为了允许函数更改其`this`上下文(例如在事件监听器中),必须使用`function`关键字,并且所提供的函数必须应用上下文。允许提供函数使用rest /spread(`...`)运算符的任意数量的参数。"
	},
	"onUserInputChange": {
		"prefix": "30s_onUserInputChange",
		"body": [
			"const onUserInputChange = callback => {",
			"  let type = 'mouse',",
			"    lastTime = 0;",
			"  const mousemoveHandler = () => {",
			"    const now = performance.now();",
			"    if (now - lastTime < 20)",
			"      (type = 'mouse'), callback(type), document.removeEventListener('mousemove', mousemoveHandler);",
			"    lastTime = now;",
			"  };",
			"  document.addEventListener('touchstart', () => {",
			"    if (type === 'touch') return;",
			"    (type = 'touch'), callback(type), document.addEventListener('mousemove', mousemoveHandler);",
			"  });",
			"};"
		],
		"description": "每当用户输入类型改变时(`mouse`或`touch`)运行回调。用于根据输入设备启用/禁用代码。此过程是动态的,适用于混合设备(例如触摸屏笔记本电脑)。 使用两个事件监听器。首先假设`mouse`输入并将`touchstart`事件监听器绑定到文档。 在`touchstart`中,添加一个`mousemove`事件监听器,使用`performance.now()`来监听在20ms内触发的两个连续`mousemove`事件。运行回调,输入类型作为其中任何一个的参数的情况。"
	},
	"orderBy": {
		"prefix": "30s_orderBy",
		"body": [
			"const orderBy = (arr, props, orders) =>",
			"  [...arr].sort((a, b) =>",
			"    props.reduce((acc, prop, i) => {",
			"      if (acc === 0) {",
			"        const [p1, p2] = orders && orders[i] === 'desc' ? [b[prop], a[prop]] : [a[prop], b[prop]];",
			"        acc = p1 > p2 ? 1 : p1 < p2 ? -1 : 0;",
			"      }",
			"      return acc;",
			"    }, 0)",
			"  );"
		],
		"description": "返回按属性和顺序排序的对象的排序数组。 在`props`数组上使用`Array.prototype.sort()`,`Array.prototype.reduce()`,默认值为`0`,使用数组解构来根据传递的顺序交换属性位置。如果没有传递`orders`数组,它默认排序为''asc'。"
	},
	"over": {
		"prefix": "30s_over",
		"body": [
			"const over = (...fns) => (...args) => fns.map(fn => fn.apply(null, args));"
		],
		"description": "创建一个函数,使用它接收的参数调用每个提供的函数并返回结果。 使用`Array.prototype.map()`和`Function.prototype.apply()`将每个函数应用于给定的参数。 "
	},
	"overArgs": {
		"prefix": "30s_overArgs",
		"body": [
			"const overArgs = (fn, transforms) => (...args) => fn(...args.map((val, i) => transforms[i](val)));"
		],
		"description": "创建一个函数,通过转换参数调用提供的函数。 使用`Array.prototype.map()`将`transforms`应用于`args`并结合扩展运算符(`...`)传递转换为`fn`。的参数"
	},
	"pad": {
		"prefix": "30s_pad",
		"body": [
			"const pad = (str, length, char = ' ') =>",
			"  str.padStart((str.length + length) / 2, char).padEnd(length, char);"
		],
		"description": "如果它比指定的长度短,则在两侧填充一个字符串。 使用`String.padStart()`和`String.padEnd()`来填充给定字符串的两边。Omit第三个参数,`char`,使用空格字符作为默认填充字符。"
	},
	"palindrome": {
		"prefix": "30s_palindrome",
		"body": [
			"const palindrome = str => {",
			"  const s = str.toLowerCase().replace(/[\\W_]/g, '');",
			"  return s === [...s].reverse().join('');",
			"};"
		],
		"description": "如果给定的字符串是回文,则返回“true”,否则返回“false”。 将字符串转换为`String.prototype.toLowerCase()`并使用`String.prototype.replace()`删除非字母数字字符然后,使用扩展运算符(`...`)将字符串拆分为单个字符,`Array.prototype.reverse()`,`String.prototype.join('')`并将其与将原始的,未反转的字符串转换为`String.prototype.toLowerCase()`。"
	},
	"parseCookie": {
		"prefix": "30s_parseCookie",
		"body": [
			"const parseCookie = str =>",
			"  str",
			"    .split(';')",
			"    .map(v => v.split('='))",
			"    .reduce((acc, v) => {",
			"      acc[decodeURIComponent(v[0].trim())] = decodeURIComponent(v[1].trim());",
			"      return acc;",
			"    }, {});"
		],
		"description": "解析HTTP Cookie头字符串并返回所有cookie名称 -值对的对象。 使用`String.prototype.split(';')`将键值对彼此分开。使用`Array.prototype .map()`和`String.prototype.split('=')`将键与每对中的值分开。使用`Array.prototype.reduce()`和`decodeURIComponent()`创建一个包含所有对象的对象键值对。"
	},
	"partial": {
		"prefix": "30s_partial",
		"body": [
			"const partial = (fn, ...partials) => (...args) => fn(...partials, ...args);"
		],
		"description": "创建一个函数,调用`fn`并将`partials`放在它接收的参数之前。 使用扩展运算符(```)将`partials`添加到`fn`的参数列表中。"
	},
	"partialRight": {
		"prefix": "30s_partialRight",
		"body": [
			"const partialRight = (fn, ...partials) => (...args) => fn(...args, ...partials);"
		],
		"description": "创建一个函数,调用`fn`并将`partials`附加到它接收的参数。 使用扩展运算符(```)将`partials`附加到`fn`的参数列表。"
	},
	"partition": {
		"prefix": "30s_partition",
		"body": [
			"const partition = (arr, fn) =>",
			"  arr.reduce(",
			"    (acc, val, i, arr) => {",
			"      acc[fn(val, i, arr) ? 0 : 1].push(val);",
			"      return acc;",
			"    },",
			"    [[], []]",
			"  );"
		],
		"description": "将元素分组为两个数组,具体取决于所提供的函数对每个元素的真实性。 使用`Array.prototype.reduce()`创建一个包含两个数组的数组。使用`Array.prototype.push()`将`fn`返回'true`的元素添加到第一个数组,将`fn`返回`false`的元素添加到第二个数组。"
	},
	"percentile": {
		"prefix": "30s_percentile",
		"body": [
			"const percentile = (arr, val) =>",
			"  (100 * arr.reduce((acc, v) => acc + (v < val ? 1 : 0) + (v === val ? 0.5 : 0), 0)) / arr.length;"
		],
		"description": "使用百分位公式计算给定数组中的数量是否小于或等于给定值。 使用`Array.prototype.reduce()`来计算低于该值的数量以及相同数量的数量价值并应用百分位公式。"
	},
	"permutations": {
		"prefix": "30s_permutations",
		"body": [
			"const permutations = arr => {",
			"  if (arr.length <= 2) return arr.length === 2 ? [arr, [arr[1], arr[0]]] : arr;",
			"  return arr.reduce(",
			"    (acc, item, i) =>",
			"      acc.concat(",
			"        permutations([...arr.slice(0, i), ...arr.slice(i + 1)]).map(val => [item, ...val])",
			"      ),",
			"    []",
			"  );",
			"};"
		],
		"description": "⚠️**警告**:此函数的执行时间随每个数组元素呈指数增长。超过8到10个条目的任何内容都会导致浏览器在尝试解决所有不同组合时挂起。 生成数组元素的所有排列(包含重复项)。 使用递归。对于每个元素给定数组,为其余元素创建所有部分排列。使用`Array.prototype.map()`将元素与每个部分排列组合,然后使用`Array.prototype.reduce()`组合所有排列一个数组。基本情况是数组`length`等于`2`或`1`。"
	},
	"pick": {
		"prefix": "30s_pick",
		"body": [
			"const pick = (obj, arr) =>",
			"  arr.reduce((acc, curr) => (curr in obj && (acc[curr] = obj[curr]), acc), {});"
		],
		"description": "从对象中选取与给定键对应的键值对。 使用`Array.prototype.reduce()`将已过滤/拾取的键转换回具有相应键值对的对象(如果键存在)在对象中。"
	},
	"pickBy": {
		"prefix": "30s_pickBy",
		"body": [
			"const pickBy = (obj, fn) =>",
			"  Object.keys(obj)",
			"    .filter(k => fn(obj[k], k))",
			"    .reduce((acc, key) => ((acc[key] = obj[key]), acc), {});"
		],
		"description": "创建一个由给定函数返回truthy的属性组成的对象。该函数使用两个参数调用:(value,key)。 使用`Object.keys(obj)`和`Array.prototype.filter()`删除`fn`返回伪值的键。 使用`Array.prototype.reduce()`将过滤后的密钥转换回具有相应键值对的对象。"
	},
	"pipeAsyncFunctions": {
		"prefix": "30s_pipeAsyncFunctions",
		"body": [
			"const pipeAsyncFunctions = (...fns) => arg => fns.reduce((p, f) => p.then(f), Promise.resolve(arg));"
		],
		"description": "为异步函数执行从左到右的函数组合。 使用带有扩展运算符(`...`)的`Array.prototype.reduce()`,使用`Promise.then执行从左到右的函数组合。 ()`。函数可以返回以下组合:简单值,`Promise`,或者它们可以定义为通过`await`返回的`async`。所有函数必须是一元的。"
	},
	"pipeFunctions": {
		"prefix": "30s_pipeFunctions",
		"body": [
			"const pipeFunctions = (...fns) => fns.reduce((f, g) => (...args) => g(f(...args)));"
		],
		"description": "执行从左到右的函数组合。 使用带有扩展运算符(`...`)的`Array.prototype.reduce()`来执行从左到右的函数组合。第一个(最左边)函数可以接受一个或多个论点;其余的功能必须是一元的。"
	},
	"pluralize": {
		"prefix": "30s_pluralize",
		"body": [
			"const pluralize = (val, word, plural = word + 's') => {",
			"  const _pluralize = (num, word, plural = word + 's') =>",
			"    [1, -1].includes(Number(num)) ? word : plural;",
			"  if (typeof val === 'object') return (num, word) => _pluralize(num, word, val[word]);",
			"  return _pluralize(val, word, plural);",
			"};"
		],
		"description": "根据输入数字返回单词或复数形式的单词。如果第一个参数是一个`object`,它将使用一个闭包,它返回一个函数,如果提供的字典包含单词,它可以自动复制不仅仅以`s`结尾的单词。 如果`num`是“-1”或“1”,返回单词的单数形式。如果`num`是任何其他数字,则返回复数形式。省略第三个参数以使用单数词+`s`的默认值,或在必要时提供自定义复数词。如果第一个参数是`object`,则通过返回一个函数来使用闭包,该函数可以使用提供的字典来解析单词的正确复数形式。"
	},
	"powerset": {
		"prefix": "30s_powerset",
		"body": [
			"const powerset = arr => arr.reduce((a, v) => a.concat(a.map(r => [v].concat(r))), [[]]);"
		],
		"description": "返回给定数字数组的powerset。 使用`Array.prototype.reduce()`结合`Array.prototype.map()`迭代元素并组合成一个包含所有组合的数组。"
	},
	"prefix": {
		"prefix": "30s_prefix",
		"body": [
			"const prefix = prop => {",
			"  const capitalizedProp = prop.charAt(0).toUpperCase() + prop.slice(1);",
			"  const prefixes = ['', 'webkit', 'moz', 'ms', 'o'];",
			"  const i = prefixes.findIndex(",
			"    prefix => typeof document.body.style[prefix ? prefix + capitalizedProp : prop] !== 'undefined'",
			"  );",
			"  return i !== -1 ? (i === 0 ? prop : prefixes[i] + capitalizedProp) : null;",
			"};"
		],
		"description": "返回浏览器支持的CSS属性的前缀版本(如果需要)。 在供应商前缀字符串数组上使用`Array.prototype.findIndex()`来测试`document.body`是否定义了其中一个在`CSSStyleDeclaration`对象中,否则返回`null`。使用`String.prototype.charAt()`和`String.prototype.toUpperCase()`来大写属性,该属性将附加到供应商前缀字符串。 ñ"
	},
	"prettyBytes": {
		"prefix": "30s_prettyBytes",
		"body": [
			"const prettyBytes = (num, precision = 3, addSpace = true) => {",
			"  const UNITS = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];",
			"  if (Math.abs(num) < 1) return num + (addSpace ? ' ' : '') + UNITS[0];",
			"  const exponent = Math.min(Math.floor(Math.log10(num < 0 ? -num : num) / 3), UNITS.length - 1);",
			"  const n = Number(((num < 0 ? -num : num) / 1000 ** exponent).toPrecision(precision));",
			"  return (num < 0 ? '-' : '') + n + (addSpace ? ' ' : '') + UNITS[exponent];",
			"};"
		],
		"description": "将字节数转换为人类可读的字符串。 使用基于指数访问的单位数组字典。使用`Number.toPrecision()`将数字截断为一定数量的数字。 n通过构建它来回归美化字符串,考虑提供的选项以及它是否为负。省略第二个参数`precision`,使用默认精度为`3`数字。省略第三个参数, `addSpace`,默认情况下在数字和单位之间添加空格。"
	},
	"primes": {
		"prefix": "30s_primes",
		"body": [
			"const primes = num => {",
			"  let arr = Array.from({ length: num - 1 }).map((x, i) => i + 2),",
			"    sqroot = Math.floor(Math.sqrt(num)),",
			"    numsTillSqroot = Array.from({ length: sqroot - 1 }).map((x, i) => i + 2);",
			"  numsTillSqroot.forEach(x => (arr = arr.filter(y => y % x !== 0 || y === x)));",
			"  return arr;",
			"};"
		],
		"description": "使用Eratosthenes的Sieve生成一个给定数字的素数。 生成一个从“2”到给定数字的数组。使用`Array.prototype.filter()`过滤掉从“2”到所提供数字的平方根的任何数字可被整除的值。"
	},
	"promisify": {
		"prefix": "30s_promisify",
		"body": [
			"const promisify = func => (...args) =>",
			"  new Promise((resolve, reject) =>",
			"    func(...args, (err, result) => (err ? reject(err) : resolve(result)))",
			"  );"
		],
		"description": "转换异步函数以返回promise。  *在Node 8+中,您可以使用[`util.promisify`](https://nodejs.org/api/util.html#util_util_promisify_original)* 使用currying返回一个函数,返回调用原始函数的`Promise`。使用`... rest`运算符传递所有参数。"
	},
	"pull": {
		"prefix": "30s_pull",
		"body": [
			"const pull = (arr, ...args) => {",
			"  let argState = Array.isArray(args[0]) ? args[0] : args;",
			"  let pulled = arr.filter((v, i) => !argState.includes(v));",
			"  arr.length = 0;",
			"  pulled.forEach(v => arr.push(v));",
			"};"
		],
		"description": "改变原始数组以过滤掉指定的值。 使用`Array.prototype.filter()`和`Array.prototype.includes()`来提取不需要的值。使用`Array.prototype .length = 0`通过将其长度重置为零来改变传入的数组,并将Array.prototype.push()`重新填充为仅使用拉取的值。"
	},
	"pullAtIndex": {
		"prefix": "30s_pullAtIndex",
		"body": [
			"const pullAtIndex = (arr, pullArr) => {",
			"  let removed = [];",
			"  let pulled = arr",
			"    .map((v, i) => (pullArr.includes(i) ? removed.push(v) : v))",
			"    .filter((v, i) => !pullArr.includes(i));",
			"  arr.length = 0;",
			"  pulled.forEach(v => arr.push(v));",
			"  return removed;",
			"};"
		],
		"description": "改变原始数组以过滤掉指定索引处的值。 使用`Array.prototype.filter()`和`Array.prototype.includes()`来提取不需要的值。使用` Array.prototype.length = 0`通过将其长度重置为零来改变传入的数组,并使用`Array.prototype.push()`来仅使用拉取的值重新填充它。使用`Array.prototype.push ()`跟踪拉出的值"
	},
	"pullAtValue": {
		"prefix": "30s_pullAtValue",
		"body": [
			"const pullAtValue = (arr, pullArr) => {",
			"  let removed = [],",
			"    pushToRemove = arr.forEach((v, i) => (pullArr.includes(v) ? removed.push(v) : v)),",
			"    mutateTo = arr.filter((v, i) => !pullArr.includes(v));",
			"  arr.length = 0;",
			"  mutateTo.forEach(v => arr.push(v));",
			"  return removed;",
			"};"
		],
		"description": "改变原始数组以过滤掉指定的值。返回已删除的元素。 使用`Array.prototype.filter()`和`Array.prototype.includes()`来提取不需要的值。使用`Array.prototype.length = 0`到通过将其长度重置为零来修改传入的数组,并使用“Array.prototype.push()”来重新填充它,仅使用拉取的值。使用`Array.prototype.push()`来跟踪拉出的值"
	},
	"pullBy": {
		"prefix": "30s_pullBy",
		"body": [
			"const pullBy = (arr, ...args) => {",
			"  const length = args.length;",
			"  let fn = length > 1 ? args[length - 1] : undefined;",
			"  fn = typeof fn == 'function' ? (args.pop(), fn) : undefined;",
			"  let argState = (Array.isArray(args[0]) ? args[0] : args).map(val => fn(val));",
			"  let pulled = arr.filter((v, i) => !argState.includes(fn(v)));",
			"  arr.length = 0;",
			"  pulled.forEach(v => arr.push(v));",
			"};"
		],
		"description": "根据给定的迭代器函数,调整原始数组以过滤掉指定的值。 检查函数中是否提供了最后一个参数。使用`Array.prototype.map()`来应用迭代器函数`fn`所有数组元素。使用`Array.prototype.filter()`和`Array.prototype.includes()`来提取不需要的值。使用`Array.prototype.length = 0`来改变它通过将它的长度重置为零来传递一个数组,并且'Array.prototype.push()`只用拉取的值重新填充它。"
	},
	"radsToDegrees": {
		"prefix": "30s_radsToDegrees",
		"body": [
			"const radsToDegrees = rad => (rad * 180.0) / Math.PI;"
		],
		"description": "将角度从弧度转换为度数。 使用“Math.PI”和弧度到度数公式将角度从弧度转换为度数。"
	},
	"randomHexColorCode": {
		"prefix": "30s_randomHexColorCode",
		"body": [
			"const randomHexColorCode = () => {",
			"  let n = (Math.random() * 0xfffff * 1000000).toString(16);",
			"  return '#' + n.slice(0, 6);",
			"};"
		],
		"description": "生成一个随机的十六进制颜色代码。 使用`Math.random`生成一个随机的24位(6x4bits)十六进制数。使用位移,然后使用`toString(16)`。将其转换为十六进制字符串"
	},
	"randomIntArrayInRange": {
		"prefix": "30s_randomIntArrayInRange",
		"body": [
			"const randomIntArrayInRange = (min, max, n = 1) =>",
			"  Array.from({ length: n }, () => Math.floor(Math.random() * (max - min + 1)) + min);"
		],
		"description": "返回指定范围内的n个随机整数数组。 使用`Array.from()`创建一个特定长度的空数组,`Math.random()`生成一个随机数并将其映射到期望的范围,使用`Math.floor()`使其成为整数。"
	},
	"randomIntegerInRange": {
		"prefix": "30s_randomIntegerInRange",
		"body": [
			"const randomIntegerInRange = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min;"
		],
		"description": "返回指定范围内的随机整数。 使用`Math.random()`生成一个随机数并将其映射到所需的范围,使用`Math.floor()`使其成为整数。"
	},
	"randomNumberInRange": {
		"prefix": "30s_randomNumberInRange",
		"body": [
			"const randomNumberInRange = (min, max) => Math.random() * (max - min) + min;"
		],
		"description": "返回指定范围内的随机数。 使用`Math.random()`生成随机值,使用乘法将其映射到所需范围。"
	},
	"readFileLines": {
		"prefix": "30s_readFileLines",
		"body": [
			"const fs = require('fs');",
			"const readFileLines = filename =>",
			"  fs",
			"    .readFileSync(filename)",
			"    .toString('UTF8')",
			"    .split('\\n');"
		],
		"description": "返回指定文件中的一行数组。 在`fs`节点包中使用`readFileSync`函数从文件创建`Buffer`。使用`toString(encoding)`函数将缓冲区转换为字符串。创建一个数组来自文件内容,逐行分割文件内容(每个``)。"
	},
	"rearg": {
		"prefix": "30s_rearg",
		"body": [
			"const rearg = (fn, indexes) => (...args) => fn(...indexes.map(i => args[i]));"
		],
		"description": "创建一个调用提供的函数的函数,其参数根据指定的索引排列。 使用`Array.prototype.map()`基于`indices`结合扩展运算符重新排序参数(`... `)将转换后的参数传递给`fn`。"
	},
	"recordAnimationFrames": {
		"prefix": "30s_recordAnimationFrames",
		"body": [
			"const recordAnimationFrames = (callback, autoStart = true) => {",
			"  let running = true,",
			"    raf;",
			"  const stop = () => {",
			"    running = false;",
			"    cancelAnimationFrame(raf);",
			"  };",
			"  const start = () => {",
			"    running = true;",
			"    run();",
			"  };",
			"  const run = () => {",
			"    raf = requestAnimationFrame(() => {",
			"      callback();",
			"      if (running) run();",
			"    });",
			"  };",
			"  if (autoStart) start();",
			"  return { start, stop };",
			"};"
		],
		"description": "在每个动画帧上调用提供的回调。 使用递归。 提供`running`是'true`,继续调用`window.requestAnimationFrame()`来调用提供的回调。 使用两种方法“start”和“stop”返回一个对象,以允许手动控制录制。 允许第二个参数`autoStart`,在调用函数时隐式调用`start`。"
	},
	"redirect": {
		"prefix": "30s_redirect",
		"body": [
			"const redirect = (url, asLink = true) =>",
			"  asLink ? (window.location.href = url) : window.location.replace(url);"
		],
		"description": "重定向到指定的URL。 使用`window.location.href`或`window.location.replace()`重定向到`url`。通过第二个参数来模拟链接点击(`true`  -默认)或HTTP重定向(`false`)。"
	},
	"reducedFilter": {
		"prefix": "30s_reducedFilter",
		"body": [
			"const reducedFilter = (data, keys, fn) =>",
			"  data.filter(fn).map(el =>",
			"    keys.reduce((acc, key) => {",
			"      acc[key] = el[key];",
			"      return acc;",
			"    }, {})",
			"  );"
		],
		"description": "根据条件过滤对象数组,同时过滤掉未指定的键。 使用`Array.prototype.filter()`根据谓词`fn`过滤数组,使其返回条件对象返回一个truthy值。在过滤后的数组中,使用`Array.prototype.map()`返回新对象,使用`Array.prototype.reduce()`过滤掉未作为`keys提供的键。参数。"
	},
	"reduceSuccessive": {
		"prefix": "30s_reduceSuccessive",
		"body": [
			"const reduceSuccessive = (arr, fn, acc) =>",
			"  arr.reduce((res, val, i, arr) => (res.push(fn(res.slice(-1)[0], val, i, arr)), res), [acc]);"
		],
		"description": "对累加器和数组中的每个元素应用一个函数(从左到右),返回一个连续减少值的数组。 使用`Array.prototype.reduce()`将给定函数应用于给定数组,存储每个新结果。"
	},
	"reduceWhich": {
		"prefix": "30s_reduceWhich",
		"body": [
			"const reduceWhich = (arr, comparator = (a, b) => a - b) =>",
			"  arr.reduce((a, b) => (comparator(a, b) >= 0 ? b : a));"
		],
		"description": "在应用提供的函数设置比较规则后,返回数组的最小/最大值。 使用`Array.prototype.reduce()`结合`comparator`函数来获取数组中的相应元素。 您可以省略第二个参数`comparator`,以使用返回数组中最小元素的默认值。"
	},
	"reject": {
		"prefix": "30s_reject",
		"body": [
			"const reject = (pred, array) => array.filter((...args) => !pred(...args));"
		],
		"description": "采用谓词和数组,如`Array.prototype.filter()`,但只保留`x`,如果`pred(x)=== false`。"
	},
	"remove": {
		"prefix": "30s_remove",
		"body": [
			"const remove = (arr, func) =>",
			"  Array.isArray(arr)",
			"    ? arr.filter(func).reduce((acc, val) => {",
			"        arr.splice(arr.indexOf(val), 1);",
			"        return acc.concat(val);",
			"      }, [])",
			"    : [];"
		],
		"description": "从数组中删除给定函数返回“false”的元素。 使用`Array.prototype.filter()`查找返回truthy值的数组元素,使用`Array.prototype.reduce()`删除元素`Array.prototype.splice()`。使用三个参数(`value,index,array`)调用`func`。"
	},
	"removeNonASCII": {
		"prefix": "30s_removeNonASCII",
		"body": [
			"const removeNonASCII = str => str.replace(/[^\\x20-\\x7E]/g, '');"
		],
		"description": "删除不可打印的ASCII字符。 使用正则表达式删除不可打印的ASCII字符。"
	},
	"renameKeys": {
		"prefix": "30s_renameKeys",
		"body": [
			"const renameKeys = (keysMap, obj) =>",
			"  Object.keys(obj).reduce(",
			"    (acc, key) => ({",
			"      ...acc,",
			"      ...{ [keysMap[key] || key]: obj[key] }",
			"    }),",
			"    {}",
			"  );"
		],
		"description": "用提供的值替换多个对象键的名称。 使用`Object.keys()`结合`Array.prototype.reduce()`和扩展运算符(`...`)来获取对象的按键并根据`keysMap`重命名它们。"
	},
	"reverseString": {
		"prefix": "30s_reverseString",
		"body": [
			"const reverseString = str => [...str].reverse().join('');"
		],
		"description": "反转字符串。 使用扩展运算符(`...`)和`Array.prototype.reverse()`来反转字符串中字符的顺序。使用`String来获取字符串的字符串。 prototype.join( '')`。"
	},
	"RGBToHex": {
		"prefix": "30s_RGBToHex",
		"body": [
			"const RGBToHex = (r, g, b) => ((r << 16) + (g << 8) + b).toString(16).padStart(6, '0');"
		],
		"description": "将RGB组件的值转换为颜色代码。 使用按位左移运算符(`<<`)和`toString(16)`,然后`String.padStart(6,')将给定的RGB参数转换为十六进制字符串0')`得到一个6位十六进制值。"
	},
	"round": {
		"prefix": "30s_round",
		"body": [
			"const round = (n, decimals = 0) => Number(`${Math.round(`${n}e${decimals}`)}e-${decimals}`);"
		],
		"description": "将数字舍入到指定的位数。 使用`Math.round()`和模板文字将数字四舍五入到指定的位数。省略第二个参数,`decimals`以舍入为整数。 "
	},
	"runAsync": {
		"prefix": "30s_runAsync",
		"body": [
			"const runAsync = fn => {",
			"  const worker = new Worker(",
			"    URL.createObjectURL(new Blob([`postMessage((${fn})());`]), {",
			"      type: 'application/javascript; charset=utf-8'",
			"    })",
			"  );",
			"  return new Promise((res, rej) => {",
			"    worker.onmessage = ({ data }) => {",
			"      res(data), worker.terminate();",
			"    };",
			"    worker.onerror = err => {",
			"      rej(err), worker.terminate();",
			"    };",
			"  });",
			"};"
		],
		"description": "使用[Web Worker](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers)在单独的线程中运行函数,允许长时间运行的函数不阻止UI。  使用`Blob`对象URL创建一个新的`Worker`,其内容应该是所提供函数的字符串化版本。立即发布调用该函数的返回值。返回一个承诺,收听`onmessage`和`onerror`事件并解析从工作人员发回的数据,或者抛出错误。"
	},
	"runPromisesInSeries": {
		"prefix": "30s_runPromisesInSeries",
		"body": [
			"const runPromisesInSeries = ps => ps.reduce((p, next) => p.then(next), Promise.resolve());"
		],
		"description": "运行一系列Promise系列。 使用`Array.prototype.reduce()`创建一个promise链,其中每个promise在解析时返回下一个promise。"
	},
	"sample": {
		"prefix": "30s_sample",
		"body": [
			"const sample = arr => arr[Math.floor(Math.random() * arr.length)];"
		],
		"description": "从数组中返回一个随机元素。 使用`Math.random()`生成一个随机数,乘以`length`并使用`Math.floor()`将其四舍五入到最接近的整数。 n此方法也适用于字符串。"
	},
	"sampleSize": {
		"prefix": "30s_sampleSize",
		"body": [
			"const sampleSize = ([...arr], n = 1) => {",
			"  let m = arr.length;",
			"  while (m) {",
			"    const i = Math.floor(Math.random() * m--);",
			"    [arr[m], arr[i]] = [arr[i], arr[m]];",
			"  }",
			"  return arr.slice(0, n);",
			"};"
		],
		"description": "从`array`获取`n`随机元素到`array`的大小。 使用[Fisher-Yates算法]对数组进行处理(https://github.com/30-seconds/30 -seconds-of-code #shuffle)。使用`Array.prototype.slice()`获取第一个`n`元素。省略第二个参数,`n`从数组中只获取一个随机元素。 "
	},
	"scrollToTop": {
		"prefix": "30s_scrollToTop",
		"body": [
			"const scrollToTop = () => {",
			"  const c = document.documentElement.scrollTop || document.body.scrollTop;",
			"  if (c > 0) {",
			"    window.requestAnimationFrame(scrollToTop);",
			"    window.scrollTo(0, c - c / 8);",
			"  }",
			"};"
		],
		"description": "平滑滚动到页面顶部。 使用`document.documentElement.scrollTop`或`document.body.scrollTop`与顶部的距离。滚动距离顶部的一小部分距离。使用`window.requestAnimationFrame()`为滚动设置动画。"
	},
	"sdbm": {
		"prefix": "30s_sdbm",
		"body": [
			"const sdbm = str => {",
			"  let arr = str.split('');",
			"  return arr.reduce(",
			"    (hashCode, currentVal) =>",
			"      (hashCode = currentVal.charCodeAt(0) + (hashCode << 6) + (hashCode << 16) - hashCode),",
			"    0",
			"  );",
			"};"
		],
		"description": "将输入字符串散列为整数。 使用`String.prototype.split('')`和`Array.prototype.reduce()`来创建输入字符串的散列,利用位移。"
	},
	"serializeCookie": {
		"prefix": "30s_serializeCookie",
		"body": [
			"const serializeCookie = (name, val) => `${encodeURIComponent(name)}=${encodeURIComponent(val)}`;"
		],
		"description": "将cookie名称 -值对序列化为Set-Cookie头字符串。 使用模板文字和`encodeURIComponent()`来创建适当的字符串。"
	},
	"serializeForm": {
		"prefix": "30s_serializeForm",
		"body": [
			"const serializeForm = form =>",
			"  Array.from(new FormData(form), field => field.map(encodeURIComponent).join('=')).join('&');"
		],
		"description": "将一组表单元素编码为查询字符串。 使用`FormData`构造函数将HTML`表单`转换为`FormData`,`Array.from()`以转换为数组,将map函数作为第二个参数。使用`Array.prototype.map()`和`window.encodeURIComponent()`来编码每个字段的值。使用带有适当参数的`Array.prototype.join()`来生成一个合适的查询字符串。 "
	},
	"setStyle": {
		"prefix": "30s_setStyle",
		"body": [
			"const setStyle = (el, ruleName, val) => (el.style[ruleName] = val);"
		],
		"description": "设置指定元素的CSS规则的值。 使用`element.style`将指定元素的CSS规则的值设置为“val”。"
	},
	"shallowClone": {
		"prefix": "30s_shallowClone",
		"body": [
			"const shallowClone = obj => Object.assign({}, obj);"
		],
		"description": "创建一个对象的浅层克隆。 使用`Object.assign()`和一个空对象(`{}`)来创建原始的浅层克隆。"
	},
	"shank": {
		"prefix": "30s_shank",
		"body": [
			"const shank = (arr, index = 0, delCount = 0, ...elements) =>",
			"  arr",
			"    .slice(0, index)",
			"    .concat(elements)",
			"    .concat(arr.slice(index + delCount));"
		],
		"description": "具有与[`Array.prototype.splice()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice)相同的功能,但返回一个新的数组而不是改变原始数组。 使用`Array.prototype.slice()`和`Array.prototype.concat()`在删除现有元素和/或添加新元素后获取包含新内容的新数组。允许第二个参数`index`,从'0`开始。省略第三个参数`delCount`,删除`0`元素。省略第四个参数`elements`,以便不添加任何新元素。"
	},
	"show": {
		"prefix": "30s_show",
		"body": [
			"const show = (...el) => [...el].forEach(e => (e.style.display = ''));"
		],
		"description": "显示指定的所有元素。 使用扩展运算符(`...`)和`Array.prototype.forEach()`清除指定的每个元素的`display`属性。"
	},
	"shuffle": {
		"prefix": "30s_shuffle",
		"body": [
			"const shuffle = ([...arr]) => {",
			"  let m = arr.length;",
			"  while (m) {",
			"    const i = Math.floor(Math.random() * m--);",
			"    [arr[m], arr[i]] = [arr[i], arr[m]];",
			"  }",
			"  return arr;",
			"};"
		],
		"description": "随机化数组值的顺序,返回一个新数组。 使用[Fisher-Yates算法](https://github.com/30-seconds/30-seconds-of-code#shuffle)重新排序数组的元素。"
	},
	"similarity": {
		"prefix": "30s_similarity",
		"body": [
			"const similarity = (arr, values) => arr.filter(v => values.includes(v));"
		],
		"description": "返回出现在两个数组中的元素数组。 使用`Array.prototype.filter()`删除不属于`values`的值,使用`Array.prototype.includes()`确定。"
	},
	"size": {
		"prefix": "30s_size",
		"body": [
			"const size = val =>",
			"  Array.isArray(val)",
			"    ? val.length",
			"    : val && typeof val === 'object'",
			"    ? val.size || val.length || Object.keys(val).length",
			"    : typeof val === 'string'",
			"    ? new Blob([val]).size",
			"    : 0;"
		],
		"description": "获取数组,对象或字符串的大小。 获取`val`(`array`,`object`或`string`)的类型。 使用数组的`length`属性。如果可用,则使用`length`或`size`值或对象的键数。使用[`Blob`对象]的`size`(https://developer.mozilla。 org /en-US /docs /Web /API /Blob)从`val`为字符串创建。将字符串转换为带有`split('')`的字符数组并返回其长度。"
	},
	"sleep": {
		"prefix": "30s_sleep",
		"body": [
			"const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));"
		],
		"description": "延迟执行异步函数。 Delay执行`async`函数的一部分,通过将其置于休眠状态,返回一个`Promise`。"
	},
	"smoothScroll": {
		"prefix": "30s_smoothScroll",
		"body": [
			"const smoothScroll = element =>",
			"  document.querySelector(element).scrollIntoView({",
			"    behavior: 'smooth'",
			"  });"
		],
		"description": "平滑地将调用它的元素滚动到浏览器窗口的可见区域。 使用`.scrollIntoView`方法滚动元素。 通过`{behavior:'smooth'}`到`.scrollIntoView`,它可以顺利滚动。"
	},
	"sortCharactersInString": {
		"prefix": "30s_sortCharactersInString",
		"body": [
			"const sortCharactersInString = str => [...str].sort((a, b) => a.localeCompare(b)).join('');"
		],
		"description": "按字母顺序对字符串中的字符进行排序。 使用扩展运算符(`...`),`Array.prototype.sort()`和`String.localeCompare()`对`str`中的字符进行排序,重新组合使用`String.prototype.join('')`。"
	},
	"sortedIndex": {
		"prefix": "30s_sortedIndex",
		"body": [
			"const sortedIndex = (arr, n) => {",
			"  const isDescending = arr[0] > arr[arr.length - 1];",
			"  const index = arr.findIndex(el => (isDescending ? n >= el : n <= el));",
			"  return index === -1 ? arr.length : index;",
			"};"
		],
		"description": "返回应该将值插入到数组中的最低索引,以便维护其排序顺序。 检查数组是否按降序排序(松散)。使用`Array.prototype.findIndex()`查找应该插入元素的适当索引。"
	},
	"sortedIndexBy": {
		"prefix": "30s_sortedIndexBy",
		"body": [
			"const sortedIndexBy = (arr, n, fn) => {",
			"  const isDescending = fn(arr[0]) > fn(arr[arr.length - 1]);",
			"  const val = fn(n);",
			"  const index = arr.findIndex(el => (isDescending ? val >= fn(el) : val <= fn(el)));",
			"  return index === -1 ? arr.length : index;",
			"};"
		],
		"description": "返回应该将值插入数组的最低索引,以便根据提供的迭代器函数维护其排序顺序。 检查数组是否按降序排序(松散)。使用`Array.prototype。 findIndex()`根据迭代器函数`fn`找到应该插入元素的适当索引。"
	},
	"sortedLastIndex": {
		"prefix": "30s_sortedLastIndex",
		"body": [
			"const sortedLastIndex = (arr, n) => {",
			"  const isDescending = arr[0] > arr[arr.length - 1];",
			"  const index = arr.reverse().findIndex(el => (isDescending ? n <= el : n >= el));",
			"  return index === -1 ? 0 : arr.length - index;",
			"};"
		],
		"description": "返回应该将值插入数组以保持其排序顺序的最高索引。 检查数组是否按降序排序(松散)。使用`Array.prototype.reverse()`和`Array .prototype.findIndex()`找到应该插入元素的最后一个索引。"
	},
	"sortedLastIndexBy": {
		"prefix": "30s_sortedLastIndexBy",
		"body": [
			"const sortedLastIndexBy = (arr, n, fn) => {",
			"  const isDescending = fn(arr[0]) > fn(arr[arr.length - 1]);",
			"  const val = fn(n);",
			"  const index = arr",
			"    .map(fn)",
			"    .reverse()",
			"    .findIndex(el => (isDescending ? val <= el : val >= el));",
			"  return index === -1 ? 0 : arr.length - index;",
			"};"
		],
		"description": "返回应该将值插入数组的最高索引,以便根据提供的迭代器函数维护其排序顺序。 检查数组是否按降序排序(松散)。使用`Array.prototype。 map()`将迭代器函数应用于数组的所有元素。使用`Array.prototype.reverse()`和`Array.prototype.findIndex()`来查找应该插入元素的最后一个索引,基于提供的迭代器函数。"
	},
	"splitLines": {
		"prefix": "30s_splitLines",
		"body": [
			"const splitLines = str => str.split(/\\r?\\n/);"
		],
		"description": "将多行字符串拆分为一个行数组。 使用`String.prototype.split()`和一个正则表达式来匹配换行符并创建一个数组。"
	},
	"spreadOver": {
		"prefix": "30s_spreadOver",
		"body": [
			"const spreadOver = fn => argsArr => fn(...argsArr);"
		],
		"description": "采用一个可变函数并返回一个闭包,该闭包接受一个参数数组以映射到函数的输入。 使用闭包和扩展运算符(`...`)将参数数组映射到输入功能。"
	},
	"stableSort": {
		"prefix": "30s_stableSort",
		"body": [
			"const stableSort = (arr, compare) =>",
			"  arr",
			"    .map((item, index) => ({ item, index }))",
			"    .sort((a, b) => compare(a.item, b.item) || a.index - b.index)",
			"    .map(({ item }) => item);"
		],
		"description": "执行数组的稳定排序,当它们的值相同时保留项的初始索引。不要改变原始数组,而是返回一个新数组。 使用`Array.prototype.map()`来配对输入数组的每个元素及其对应的索引。使用`Array.prototype.sort()`和`compare`函数对列表进行排序,如果比较的项相等则保留它们的初始顺序。使用`Array.prototype .map()`转换回初始数组项。"
	},
	"standardDeviation": {
		"prefix": "30s_standardDeviation",
		"body": [
			"const standardDeviation = (arr, usePopulation = false) => {",
			"  const mean = arr.reduce((acc, val) => acc + val, 0) / arr.length;",
			"  return Math.sqrt(",
			"    arr.reduce((acc, val) => acc.concat((val - mean) ** 2), []).reduce((acc, val) => acc + val, 0) /",
			"      (arr.length - (usePopulation ? 0 : 1))",
			"  );",
			"};"
		],
		"description": "返回数组数组的标准偏差。 使用`Array.prototype.reduce()`来计算均值,方差和值的方差之和,值的方差,然后确定标准偏差。您可以省略第二个参数来获取样本标准偏差或将其设置为“true”以获得总体标准差。"
	},
	"stringPermutations": {
		"prefix": "30s_stringPermutations",
		"body": [
			"const stringPermutations = str => {",
			"  if (str.length <= 2) return str.length === 2 ? [str, str[1] + str[0]] : [str];",
			"  return str",
			"    .split('')",
			"    .reduce(",
			"      (acc, letter, i) =>",
			"        acc.concat(stringPermutations(str.slice(0, i) + str.slice(i + 1)).map(val => letter + val)),",
			"      []",
			"    );",
			"};"
		],
		"description": "⚠️**警告**:此函数的执行时间随每个字符呈指数增长。超过8到10个字符的任何内容都会导致浏览器在尝试解决所有不同组合时挂起。 生成字符串的所有排列(包含重复项)。 使用递归。对于给定的每个字母string,为其余字母创建所有部分排列。使用`Array.prototype.map()`将字母与每个部分排列组合,然后使用`Array.prototype.reduce()`将所有排列组合在一起数组。基本情况是字符串`length`等于`2`或`1`。"
	},
	"stripHTMLTags": {
		"prefix": "30s_stripHTMLTags",
		"body": [
			"const stripHTMLTags = str => str.replace(/<[^>]*>/g, '');"
		],
		"description": "从字符串中删除HTML /XML标记。 使用正则表达式从字符串中删除HTML /XML标记。"
	},
	"sum": {
		"prefix": "30s_sum",
		"body": [
			"const sum = (...arr) => [...arr].reduce((acc, val) => acc + val, 0);"
		],
		"description": "返回两个或多个数字/数组的总和。 使用`Array.prototype.reduce()`将每个值添加到累加器,使用值“0”初始化。"
	},
	"sumBy": {
		"prefix": "30s_sumBy",
		"body": [
			"const sumBy = (arr, fn) =>",
			"  arr.map(typeof fn === 'function' ? fn : val => val[fn]).reduce((acc, val) => acc + val, 0);"
		],
		"description": "在使用提供的函数将每个元素映射到一个值之后,返回一个数组的总和。 使用`Array.prototype.map()`将每个元素映射到`fn`,`Array.prototype返回的值。 reduce()`将每个值添加到累加器,用值“0”初始化。"
	},
	"sumPower": {
		"prefix": "30s_sumPower",
		"body": [
			"const sumPower = (end, power = 2, start = 1) =>",
			"  Array(end + 1 - start)",
			"    .fill(0)",
			"    .map((x, i) => (i + start) ** power)",
			"    .reduce((a, b) => a + b, 0);"
		],
		"description": "返回从“start”到“end”(包括两者)的所有数字的幂的总和。 使用`Array.prototype.fill()`创建一个包含目标范围内所有数字的数组, Array.prototype.map()`和指数运算符(`**`)将它们提升到`power`和`Array.prototype.reduce()`以将它们加在一起。省略第二个参数,`power`,使用默认功率“2”。省略第三个参数`start`,使用默认的起始值`1`。"
	},
	"symmetricDifference": {
		"prefix": "30s_symmetricDifference",
		"body": [
			"const symmetricDifference = (a, b) => {",
			"  const sA = new Set(a),",
			"    sB = new Set(b);",
			"  return [...a.filter(x => !sB.has(x)), ...b.filter(x => !sA.has(x))];",
			"};"
		],
		"description": "返回两个数组之间的对称差异,而不过滤掉重复的值。 从每个数组创建一个`Set`,然后在每个数组上使用`Array.prototype.filter()`只保留不包含在另一个数组中的值。"
	},
	"symmetricDifferenceBy": {
		"prefix": "30s_symmetricDifferenceBy",
		"body": [
			"const symmetricDifferenceBy = (a, b, fn) => {",
			"  const sA = new Set(a.map(v => fn(v))),",
			"    sB = new Set(b.map(v => fn(v)));",
			"  return [...a.filter(x => !sB.has(fn(x))), ...b.filter(x => !sA.has(fn(x)))];",
			"};"
		],
		"description": "在将提供的函数应用于两个数组的每个数组元素之后,返回两个数组之间的对称差异。 通过将`fn`应用于每个数组的元素来创建`Set`,然后使用`Array.prototype.filter()`on它们中的每一个都只保留不包含在另一个中的值。"
	},
	"symmetricDifferenceWith": {
		"prefix": "30s_symmetricDifferenceWith",
		"body": [
			"const symmetricDifferenceWith = (arr, val, comp) => [",
			"  ...arr.filter(a => val.findIndex(b => comp(a, b)) === -1),",
			"  ...val.filter(a => arr.findIndex(b => comp(a, b)) === -1)",
			"];"
		],
		"description": "使用提供的函数作为比较器返回两个数组之间的对称差异。 使用`Array.prototype.filter()`和`Array.prototype.findIndex()`来查找适当的值。"
	},
	"tail": {
		"prefix": "30s_tail",
		"body": [
			"const tail = arr => (arr.length > 1 ? arr.slice(1) : arr);"
		],
		"description": "返回数组中除第一个元素之外的所有元素。 返回`Array.prototype.slice(1)`如果数组的`length`大于`1`,否则返回整个数组。"
	},
	"take": {
		"prefix": "30s_take",
		"body": [
			"const take = (arr, n = 1) => arr.slice(0, n);"
		],
		"description": "返回一个从开头删除了n个元素的数组。 使用`Array.prototype.slice()`创建一个数组的切片,从头开始采用`n`个元素。"
	},
	"takeRight": {
		"prefix": "30s_takeRight",
		"body": [
			"const takeRight = (arr, n = 1) => arr.slice(arr.length - n, arr.length);"
		],
		"description": "返回一个从末尾删除n个元素的数组。 使用`Array.prototype.slice()`创建一个数组切片,从末尾取出`n`个元素。"
	},
	"takeRightWhile": {
		"prefix": "30s_takeRightWhile",
		"body": [
			"const takeRightWhile = (arr, func) =>",
			"  arr.reduceRight((acc, el) => (func(el) ? acc : [el, ...acc]), []);"
		],
		"description": "从数组末尾删除元素,直到传递的函数返回“true”。返回已删除的元素。 通过数组,使用`Array.prototype.reduceRight()`并在函数返回falsy值时累积元素。"
	},
	"takeWhile": {
		"prefix": "30s_takeWhile",
		"body": [
			"const takeWhile = (arr, func) => {",
			"  for (const [i, val] of arr.entries()) if (func(val)) return arr.slice(0, i);",
			"  return arr;",
			"};"
		],
		"description": "删除数组中的元素,直到传递的函数返回“true”。返回已删除的元素。 通过数组,使用`for ... of`循环遍历`Array.prototype.entries()`直到函数的返回值为“true”。返回已删除的元素,使用`Array.prototype.slice()`。"
	},
	"throttle": {
		"prefix": "30s_throttle",
		"body": [
			"const throttle = (fn, wait) => {",
			"  let inThrottle, lastFn, lastTime;",
			"  return function() {",
			"    const context = this,",
			"      args = arguments;",
			"    if (!inThrottle) {",
			"      fn.apply(context, args);",
			"      lastTime = Date.now();",
			"      inThrottle = true;",
			"    } else {",
			"      clearTimeout(lastFn);",
			"      lastFn = setTimeout(function() {",
			"        if (Date.now() - lastTime >= wait) {",
			"          fn.apply(context, args);",
			"          lastTime = Date.now();",
			"        }",
			"      }, Math.max(wait - (Date.now() - lastTime), 0));",
			"    }",
			"  };",
			"};"
		],
		"description": "创建一个限制函数,每个`wait`毫秒最多只调用一次提供的函数 使用`setTimeout()`和`clearTimeout()`来限制给定的方法,`fn`。使用`Function.prototype .apply()`将`this`上下文应用于函数并提供必要的`参数`。使用`Date.now()`来跟踪上次调用受限函数的时间。省略第二个参数,`wait`,将超时设置为默认值0 ms。"
	},
	"times": {
		"prefix": "30s_times",
		"body": [
			"const times = (n, fn, context = undefined) => {",
			"  let i = 0;",
			"  while (fn.call(context, i) !== false && ++i < n) {}",
			"};"
		],
		"description": "迭代回调`n`次 使用`Function.call()`来调用`fn``n`次或直到它返回`false`。忽略最后一个参数`context`,使用`` undefined`对象(或非严格模式下的全局对象)。"
	},
	"timeTaken": {
		"prefix": "30s_timeTaken",
		"body": [
			"const timeTaken = callback => {",
			"  console.time('timeTaken');",
			"  const r = callback();",
			"  console.timeEnd('timeTaken');",
			"  return r;",
			"};"
		],
		"description": "测量函数执行所花费的时间。 使用`console.time()`和`console.timeEnd()`来测量开始和结束时间之间的差异,以确定回调执行的时间。 ñ"
	},
	"toCamelCase": {
		"prefix": "30s_toCamelCase",
		"body": [
			"const toCamelCase = str => {",
			"  let s =",
			"    str &&",
			"    str",
			"      .match(/[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+/g)",
			"      .map(x => x.slice(0, 1).toUpperCase() + x.slice(1).toLowerCase())",
			"      .join('');",
			"  return s.slice(0, 1).toLowerCase() + s.slice(1);",
			"};"
		],
		"description": "将字符串转换为camelcase。 将字符串转换为单词并将它们组合起来,使用正则表达式将每个单词的第一个字母大写。"
	},
	"toCurrency": {
		"prefix": "30s_toCurrency",
		"body": [
			"const toCurrency = (n, curr, LanguageFormat = undefined) =>",
			"  Intl.NumberFormat(LanguageFormat, { style: 'currency', currency: curr }).format(n);"
		],
		"description": "获取一个数字并返回指定的货币格式。 使用`Intl.NumberFormat`启用国家/货币敏感格式。"
	},
	"toDecimalMark": {
		"prefix": "30s_toDecimalMark",
		"body": [
			"const toDecimalMark = num => num.toLocaleString('en-US');"
		],
		"description": "使用`toLocaleString()`将浮点运算转换为[十进制标记](https://en.wikipedia.org/wiki/Decimal_mark)表单。它使用逗号分隔的字符串与数字。"
	},
	"toggleClass": {
		"prefix": "30s_toggleClass",
		"body": [
			"const toggleClass = (el, className) => el.classList.toggle(className);"
		],
		"description": "切换元素的类。 使用`element.classList.toggle()`切换元素的指定类。"
	},
	"toHash": {
		"prefix": "30s_toHash",
		"body": [
			"const toHash = (object, key) =>",
			"  Array.prototype.reduce.call(",
			"    object,",
			"    (acc, data, index) => ((acc[!key ? index : data[key]] = data), acc),",
			"    {}",
			"  );"
		],
		"description": "将给定的数组类型减少为值哈希值(键控数据存储)。 如果给出了一个Iterable或类似数组的结构,请在提供的对象上调用`Array.prototype.reduce.call()`来跳过它并返回一个对象,由参考值键入。"
	},
	"toKebabCase": {
		"prefix": "30s_toKebabCase",
		"body": [
			"const toKebabCase = str =>",
			"  str &&",
			"  str",
			"    .match(/[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+/g)",
			"    .map(x => x.toLowerCase())",
			"    .join('-');"
		],
		"description": "将字符串转换为kebab case。并将字符串分解为单词并将它们组合使用正则表达式将`-`添加为分隔符。"
	},
	"tomorrow": {
		"prefix": "30s_tomorrow",
		"body": [
			"const tomorrow = () => {",
			"  let t = new Date();",
			"  t.setDate(t.getDate() + 1);",
			"  return t.toISOString().split('T')[0];",
			"};"
		],
		"description": "结果显示明天日期的字符串表示。 使用`new Date()`获取当前日期,使用`Date.getDate()`递增1并使用`Date.setDate()将值设置为结果`。 使用`Date.prototype.toISOString()`返回`yyyy-mm-dd`格式的字符串。"
	},
	"toOrdinalSuffix": {
		"prefix": "30s_toOrdinalSuffix",
		"body": [
			"const toOrdinalSuffix = num => {",
			"  const int = parseInt(num),",
			"    digits = [int % 10, int % 100],",
			"    ordinals = ['st', 'nd', 'rd', 'th'],",
			"    oPattern = [1, 2, 3, 4],",
			"    tPattern = [11, 12, 13, 14, 15, 16, 17, 18, 19];",
			"  return oPattern.includes(digits[0]) && !tPattern.includes(digits[1])",
			"    ? int + ordinals[digits[0] - 1]",
			"    : int + ordinals[3];",
			"};"
		],
		"description": "为数字添加一个序数后缀。 使用模运算符(`%`)查找单个和十位数的值。查找哪些序数模式数字匹配。如果在青少年模式中找到数字,请使用青少年序数。 "
	},
	"toSafeInteger": {
		"prefix": "30s_toSafeInteger",
		"body": [
			"const toSafeInteger = num =>",
			"  Math.round(Math.max(Math.min(num, Number.MAX_SAFE_INTEGER), Number.MIN_SAFE_INTEGER));"
		],
		"description": "将值转换为安全整数。 使用`Math.max()`和`Math.min()`来查找最接近的安全值。使用`Math.round()`转换为整数。 ñ"
	},
	"toSnakeCase": {
		"prefix": "30s_toSnakeCase",
		"body": [
			"const toSnakeCase = str =>",
			"  str &&",
			"  str",
			"    .match(/[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+/g)",
			"    .map(x => x.toLowerCase())",
			"    .join('_');"
		],
		"description": "将字符串转换为snake case。并将字符串拆分为单词并将它们组合使用正则表达式将`_`添加为分隔符。"
	},
	"toTitleCase": {
		"prefix": "30s_toTitleCase",
		"body": [
			"const toTitleCase = str =>",
			"  str",
			"    .match(/[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+/g)",
			"    .map(x => x.charAt(0).toUpperCase() + x.slice(1))",
			"    .join(' ');"
		],
		"description": "将字符串转换为标题大小写。并使用正则表达式将字符串分解为单词,并将它们组合为大写每个单词的第一个字母并在它们之间添加空格。"
	},
	"transform": {
		"prefix": "30s_transform",
		"body": [
			"const transform = (obj, fn, acc) => Object.keys(obj).reduce((a, k) => fn(a, obj[k], k, obj), acc);"
		],
		"description": "对累加器和对象中的每个键应用一个函数(从左到右)。 使用`Object.keys(obj)`迭代对象中的每个键,`Array.prototype.reduce()`到对给定的累加器调用apply指定的函数。"
	},
	"triggerEvent": {
		"prefix": "30s_triggerEvent",
		"body": [
			"const triggerEvent = (el, eventType, detail) =>",
			"  el.dispatchEvent(new CustomEvent(eventType, { detail }));"
		],
		"description": "触发给定元素上的特定事件,可选地传递自定义数据。 使用`new CustomEvent()`从指定的`eventType`和详细信息创建事件。使用`el.dispatchEvent()`来触发新的在给定元素上创建事件。如果您不想将自定义数据传递给触发事件,则发出第三个参数`detail`。"
	},
	"truncateString": {
		"prefix": "30s_truncateString",
		"body": [
			"const truncateString = (str, num) =>",
			"  str.length > num ? str.slice(0, num > 3 ? num - 3 : num) + '...' : str;"
		],
		"description": "截断一个指定长度的字符串。 确定字符串的`length`是否大于`num`。将截断的字符串截断为所需的长度,并在末尾添加“...”。原始字符串。"
	},
	"truthCheckCollection": {
		"prefix": "30s_truthCheckCollection",
		"body": [
			"const truthCheckCollection = (collection, pre) => collection.every(obj => obj[pre]);"
		],
		"description": "检查谓词(第二个参数)是否对集合的所有元素(第一个参数)都是真实的。 使用`Array.prototype.every()`来检查每个传递的对象是否具有指定的属性,以及它是否返回truthy值。"
	},
	"unary": {
		"prefix": "30s_unary",
		"body": [
			"const unary = fn => val => fn(val);"
		],
		"description": "创建一个最多接受一个参数的函数,忽略任何其他参数。 调用提供的函数`fn`,只给出第一个参数。"
	},
	"uncurry": {
		"prefix": "30s_uncurry",
		"body": [
			"const uncurry = (fn, n = 1) => (...args) => {",
			"  const next = acc => args => args.reduce((x, y) => x(y), acc);",
			"  if (n > args.length) throw new RangeError('Arguments too few!');",
			"  return next(fn)(args.slice(0, n));",
			"};"
		],
		"description": "取消深度`n`的函数。 返回一个可变函数。在提供的参数上使用`Array.prototype.reduce()`来调用函数的每个后续curry级别。如果`length`为提供的参数小于`n`抛出错误。否则,使用`Array.prototype.slice(0,n)`调用带有适当数量参数的`fn`。省略第二个参数,`n` ,以达到深度“1”。"
	},
	"unescapeHTML": {
		"prefix": "30s_unescapeHTML",
		"body": [
			"const unescapeHTML = str =>",
			"  str.replace(",
			"    /&amp;|&lt;|&gt;|&#39;|&quot;/g,",
			"    tag =>",
			"      ({",
			"        '&amp;': '&',",
			"        '&lt;': '<',",
			"        '&gt;': '>',",
			"        '&#39;': \"'\",",
			"        '&quot;': '\"'",
			"      }[tag] || tag)",
			"  );"
		],
		"description": "Unescapes转义了HTML字符。 使用带有正则表达式的String.prototype.replace()`,该正则表达式匹配需要转义的字符,使用回调函数使用字典替换每个转义字符实例及其关联的非转义字符(对象)。"
	},
	"unflattenObject": {
		"prefix": "30s_unflattenObject",
		"body": [
			"const unflattenObject = obj =>",
			"  Object.keys(obj).reduce((acc, k) => {",
			"    if (k.indexOf('.') !== -1) {",
			"      const keys = k.split('.');",
			"      Object.assign(",
			"        acc,",
			"        JSON.parse(",
			"          '{' +",
			"            keys.map((v, i) => (i !== keys.length - 1 ? `\"${v}\":{` : `\"${v}\":`)).join('') +",
			"            obj[k] +",
			"            '}'.repeat(keys.length)",
			"        )",
			"      );",
			"    } else acc[k] = obj[k];",
			"    return acc;",
			"  }, {});"
		],
		"description": "使用键的路径展开对象。 使用`Object.keys(obj)`结合`Array.prototype.reduce()`将展平路径节点转换为叶节点。如果键的值包含一个点分隔符(`.`),使用`Array.prototype.split('。')`,字符串转换和`JSON.parse()`来创建一个对象,然后`Object.assign()`来创建一个对象节点。否则,将相应的键值对添加到累加器对象。"
	},
	"unfold": {
		"prefix": "30s_unfold",
		"body": [
			"const unfold = (fn, seed) => {",
			"  let result = [],",
			"    val = [null, seed];",
			"  while ((val = fn(val[1]))) result.push(val[0]);",
			"  return result;",
			"};"
		],
		"description": "使用迭代器函数和初始种子值构建数组。 使用`while`循环和`Array.prototype.push()`重复调用函数,直到它返回`false`。迭代器函数接受一个参数(`seed`)并且必须始终返回一个带有两个元素([`value`,`nextSeed`])或`false`的数组来终止。"
	},
	"union": {
		"prefix": "30s_union",
		"body": [
			"const union = (a, b) => Array.from(new Set([...a, ...b]));"
		],
		"description": "返回两个数组中任何一个中存在的每个元素。 使用`a`和`b`的所有值创建一个`Set`并转换为数组。"
	},
	"unionBy": {
		"prefix": "30s_unionBy",
		"body": [
			"const unionBy = (a, b, fn) => {",
			"  const s = new Set(a.map(fn));",
			"  return Array.from(new Set([...a, ...b.filter(x => !s.has(fn(x)))]));",
			"};"
		],
		"description": "在将提供的函数应用于两者的每个数组元素之后,返回两个数组中任何一个中存在的每个元素。 通过将所有`fn`应用于`a`的所有值来创建`Set`。创建一个````来自`a`和'b`中的所有元素,其值在应用`fn`后与先前创建的集合中的值不匹配。返回转换为数组的最后一组。"
	},
	"unionWith": {
		"prefix": "30s_unionWith",
		"body": [
			"const unionWith = (a, b, comp) =>",
			"  Array.from(new Set([...a, ...b.filter(x => a.findIndex(y => comp(x, y)) === -1)]));"
		],
		"description": "使用提供的比较器函数返回两个数组中任何一个中存在的每个元素。 使用`a`的所有值和`b`中的值创建`Set`,比较器在`a中找不到匹配项`,使用`Array.prototype.findIndex()`。"
	},
	"uniqueElements": {
		"prefix": "30s_uniqueElements",
		"body": [
			"const uniqueElements = arr => [...new Set(arr)];"
		],
		"description": "返回数组的所有唯一值。 使用ES6`Set`和`... rest`运算符来丢弃所有重复的值。"
	},
	"uniqueElementsBy": {
		"prefix": "30s_uniqueElementsBy",
		"body": [
			"const uniqueElementsBy = (arr, fn) =>",
			"  arr.reduce((acc, v) => {",
			"    if (!acc.some(x => fn(v, x))) acc.push(v);",
			"    return acc;",
			"  }, []);"
		],
		"description": "基于提供的比较器函数返回数组的所有唯一值。 使用`Array.prototype.reduce()`和`Array.prototype.some()`表示只包含每个值的第一个唯一出现的数组,基于比较器函数,`fn`。比较器函数有两个参数:被比较的两个元素的值。"
	},
	"uniqueElementsByRight": {
		"prefix": "30s_uniqueElementsByRight",
		"body": [
			"const uniqueElementsByRight = (arr, fn) =>",
			"  arr.reduceRight((acc, v) => {",
			"    if (!acc.some(x => fn(v, x))) acc.push(v);",
			"    return acc;",
			"  }, []);"
		],
		"description": "基于提供的比较器函数,从右侧开始返回数组的所有唯一值。 使用`Array.prototype.reduceRight()`和`Array.prototype.some()`表示只包含最后一个的数组每个值的唯一出现,基于比较器函数,`fn`。比较器函数有两个参数:被比较的两个元素的值。"
	},
	"uniqueSymmetricDifference": {
		"prefix": "30s_uniqueSymmetricDifference",
		"body": [
			"const uniqueSymmetricDifference = (a, b) => [",
			"  ...new Set([...a.filter(v => !b.includes(v)), ...b.filter(v => !a.includes(v))])",
			"];"
		],
		"description": "返回两个数组之间唯一的对称差异,不包含任何一个数组的重复值。 在每个数组上使用`Array.prototype.filter()`和`Array.prototype.includes()`来删除另一个数组中包含的值,然后从结果中创建一个“Set”,删除重复的值。"
	},
	"untildify": {
		"prefix": "30s_untildify",
		"body": [
			"const untildify = str => str.replace(/^~($|\\/|\\\\)/, `${require('os').homedir()}$1`);"
		],
		"description": "将波形路径转换为绝对路径。 使用带有正则表达式的`String.prototype.replace()`和`OS.homedir()`来用主目录替换路径开头的`~` 。"
	},
	"unzip": {
		"prefix": "30s_unzip",
		"body": [
			"const unzip = arr =>",
			"  arr.reduce(",
			"    (acc, val) => (val.forEach((v, i) => acc[i].push(v)), acc),",
			"    Array.from({",
			"      length: Math.max(...arr.map(x => x.length))",
			"    }).map(x => [])",
			"  );"
		],
		"description": "创建一个数组数组,取消组合由[zip](#zip)生成的数组中的元素。 使用`Math.max.apply()`获取数组中最长的子数组,`Array.prototype.map ()`使每个元素成为一个数组。使用`Array.prototype.reduce()`和`Array.prototype.forEach()`将分组值映射到各个数组。"
	},
	"unzipWith": {
		"prefix": "30s_unzipWith",
		"body": [
			"const unzipWith = (arr, fn) =>",
			"  arr",
			"    .reduce(",
			"      (acc, val) => (val.forEach((v, i) => acc[i].push(v)), acc),",
			"      Array.from({",
			"        length: Math.max(...arr.map(x => x.length))",
			"      }).map(x => [])",
			"    )",
			"    .map(val => fn(...val));"
		],
		"description": "创建一个元素数组,将[zip](#zip)生成的数组中的元素取消组合并应用提供的函数。 使用`Math.max.apply()`来获取数组中最长的子数组, Array.prototype.map()`使每个元素成为一个数组。使用`Array.prototype.reduce()`和`Array.prototype.forEach()`将分组值映射到各个数组。使用`Array.prototype .map()`和扩展运算符(`...`)将`fn`应用于每个单独的元素组。"
	},
	"URLJoin": {
		"prefix": "30s_URLJoin",
		"body": [
			"const URLJoin = (...args) =>",
			"  args",
			"    .join('/')",
			"    .replace(/[\\/]+/g, '/')",
			"    .replace(/^(.+):\\//, '$1://')",
			"    .replace(/^file:/, 'file:/')",
			"    .replace(/\\/(\\?|&|#[^!])/g, '$1')",
			"    .replace(/\\?/g, '&')",
			"    .replace('&', '?');"
		],
		"description": "将所有给定的URL段连接在一起,然后规范化生成的URL。 使用`String.prototype.join('/')`组合URL段,然后用各种各样的`String.prototype.replace()`调用regexp来规范化生成的URL(删除双斜杠,为协议添加适当的斜杠,在参数之前删除斜杠,将参数与`'&''组合并规范化第一个参数分隔符)。"
	},
	"UUIDGeneratorBrowser": {
		"prefix": "30s_UUIDGeneratorBrowser",
		"body": [
			"const UUIDGeneratorBrowser = () =>",
			"  ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, c =>",
			"    (c ^ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (c / 4)))).toString(16)",
			"  );"
		],
		"description": "在浏览器中生成UUID。 使用`crypto` API生成符合[RFC4122](https://www.ietf.org/rfc/rfc4122.txt)版本4的UUID。"
	},
	"UUIDGeneratorNode": {
		"prefix": "30s_UUIDGeneratorNode",
		"body": [
			"const crypto = require('crypto');",
			"const UUIDGeneratorNode = () =>",
			"  ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, c =>",
			"    (c ^ (crypto.randomBytes(1)[0] & (15 >> (c / 4)))).toString(16)",
			"  );"
		],
		"description": "在Node.JS中生成UUID  使用`crypto` API生成符合[RFC4122](https://www.ietf.org/rfc/rfc4122.txt)版本4的UUID。"
	},
	"validateNumber": {
		"prefix": "30s_validateNumber",
		"body": [
			"const validateNumber = n => !isNaN(parseFloat(n)) && isFinite(n) && Number(n) == n;"
		],
		"description": "如果给定的值是数字,则返回`true`,否则返回`false`。 使用`!isNaN()`结合`parseFloat()`来检查参数是否为数字。使用`isFinite() `检查数字是否有限。使用`Number()`检查强制是否成立。"
	},
	"vectorDistance": {
		"prefix": "30s_vectorDistance",
		"body": [
			"const vectorDistance = (...coords) => {",
			"  let pointLength = Math.trunc(coords.length / 2);",
			"  let sum = coords",
			"    .slice(0, pointLength)",
			"    .reduce((acc, val, i) => acc + Math.pow(val - coords[pointLength + i], 2), 0);",
			"  return Math.sqrt(sum);",
			"};"
		],
		"description": "返回两个向量之间的距离。 使用`Array.prototype.reduce()`,`Math.pow()`和`Math.sqrt()`来计算两个向量之间的欧几里德距离。"
	},
	"when": {
		"prefix": "30s_when",
		"body": [
			"const when = (pred, whenTrue) => x => (pred(x) ? whenTrue(x) : x);"
		],
		"description": "针对谓词函数测试值“x”。如果是'true`,则返回`fn(x)`。否则,返回`x`。  返回一个期望单个值`x`的函数,它返回基于`pred`的适当值。"
	},
	"without": {
		"prefix": "30s_without",
		"body": [
			"const without = (arr, ...args) => arr.filter(v => !args.includes(v));"
		],
		"description": "过滤掉具有指定值之一的数组元素。 使用`Array.prototype.filter()`创建一个数组,排除(使用`!Array.includes()`)所有给定的值。 ñ"
	},
	"words": {
		"prefix": "30s_words",
		"body": [
			"const words = (str, pattern = /[^a-zA-Z-]+/) => str.split(pattern).filter(Boolean);"
		],
		"description": "将给定的字符串转换为单词数组。 使用提供的模式(默认为非alpha作为正则表达式)的`String.prototype.split()`转换为字符串数组。使用`Array.prototype.filter()`删除任何空字符串。允许第二个参数使用默认的regexp。"
	},
	"xProd": {
		"prefix": "30s_xProd",
		"body": [
			"const xProd = (a, b) => a.reduce((acc, x) => acc.concat(b.map(y => [x, y])), []);"
		],
		"description": "通过从数组创建每个可能的对来创建两个中提供的新数组。 使用`Array.prototype.reduce()`,`Array.prototype.map()`和`Array.prototype.concat() `从两个数组的元素中生成每个可能的对,并将它们保存在数组中。"
	},
	"yesNo": {
		"prefix": "30s_yesNo",
		"body": [
			"const yesNo = (val, def = false) =>",
			"  /^(y|yes)$/i.test(val) ? true : /^(n|no)$/i.test(val) ? false : def;"
		],
		"description": "如果字符串是`y` /`yes`则返回`true`或如果字符串是`n` /`no`则返回`false`。 使用`RegExp.test()`来检查字符串是否计算为` y /yes`或`n /no`。省略第二个参数,`def`将默认答案设置为“no”。"
	},
	"yesterday": {
		"prefix": "30s_yesterday",
		"body": [
			"const yesterday = () => {",
			"  let t = new Date();",
			"  t.setDate(t.getDate() - 1);",
			"  return t.toISOString().split('T')[0];",
			"};"
		],
		"description": "结果是昨天日期的字符串表示。 使用`new Date()`获取当前日期,使用`Date.getDate()`减1并使用`Date.setDate()将值设置为结果`。使用`Date.prototype.toISOString()`返回`yyyy-mm-dd`格式的字符串。"
	},
	"zip": {
		"prefix": "30s_zip",
		"body": [
			"const zip = (...arrays) => {",
			"  const maxLength = Math.max(...arrays.map(x => x.length));",
			"  return Array.from({ length: maxLength }).map((_, i) => {",
			"    return Array.from({ length: arrays.length }, (_, k) => arrays[k][i]);",
			"  });",
			"};"
		],
		"description": "创建一个元素数组,根据原始数组中的位置进行分组。 使用`Math.max.apply()`获取参数中最长的数组。使用该长度作为返回值创建一个数组并使用`Array.from()`带有map-function来创建一个分组元素数组。如果参数数组的长度不同,则在没有找到值的情况下使用`undefined`。"
	},
	"zipObject": {
		"prefix": "30s_zipObject",
		"body": [
			"const zipObject = (props, values) =>",
			"  props.reduce((obj, prop, index) => ((obj[prop] = values[index]), obj), {});"
		],
		"description": "给定一组有效的属性标识符和一个值数组,返回一个将属性与值相关联的对象。 由于对象可以有未定义的值而不是未定义的属性指针,因此属性数组用于确定使用`Array.prototype.reduce()`生成的对象。"
	},
	"zipWith": {
		"prefix": "30s_zipWith",
		"body": [
			"const zipWith = (...array) => {",
			"  const fn = typeof array[array.length - 1] === 'function' ? array.pop() : undefined;",
			"  return Array.from({ length: Math.max(...array.map(a => a.length)) }, (_, i) =>",
			"    fn ? fn(...array.map(a => a[i])) : array.map(a => a[i])",
			"  );",
			"};"
		],
		"description": "创建一个元素数组,根据原始数组中的位置进行分组,并使用函数作为最后一个值来指定应如何组合分组值。 检查提供的最后一个参数是否为函数。使用`Math.max ()`获取参数中最长的数组。使用该长度作为返回值创建一个数组,并使用带有map-function的`Array.from()`来创建一个分组元素数组。如果参数的长度-arrays不同,`undefined`用于没有找到值的地方。使用每个组的元素`(... group)`调用该函数。"
	}
}
}