-
Notifications
You must be signed in to change notification settings - Fork 896
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
第 14 题:实现 lodash 的_.get #20
Comments
在 js 中经常会出现嵌套调用这种情况,如 a.b.c.d.e,但是这么写很容易抛出异常。你需要这么写 a && a.b && a.b.c && a.b.c.d && a.b.c.d.e,但是显得有些啰嗦与冗长了。特别是在 graphql 中,这种嵌套调用更是难以避免。 1)代码实现 function get(source, path, defaultValue = undefined) {
// a[3].b -> a.3.b -> [a,3,b]
// path 中也可能是数组的路径,全部转化成 . 运算符并组成数组
const paths = path.replace(/\[(\d+)\]/g, ".$1").split(".");
let result = source;
for (const p of paths) {
// 注意 null 与 undefined 取属性会报错,所以使用 Object 包装一下。
result = Object(result)[p];
if (result == undefined) {
return defaultValue;
}
}
return result;
}
// 测试用例
console.log(get({ a: null }, "a.b.c", 3)); // output: 3
console.log(get({ a: undefined }, "a", 3)); // output: 3
console.log(get({ a: null }, "a", 3)); // output: 3
console.log(get({ a: [{ b: 1 }] }, "a[0].b", 3)); // output: 1 2)代码实现 const _get = (object, keys, val) => {
return keys.split(/\./).reduce(
(o, j)=>( (o || {})[j] ),
object
) || val
}
console.log(get({ a: null }, "a.b.c", 3)); // output: 3
console.log(get({ a: undefined }, "a", 3)); // output: 3
console.log(get({ a: null }, "a", 3)); // output: 3
console.log(get({ a: { b: 1 } }, "a.b", 3)); // output: 1 |
//匹配'.'或者'[]'的深层属性
const reIsDeepProp = /\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/
//匹配普通属性:数字+字母+下划线
const reIsPlainProp = /^\w*$/
const reEscapeChar = /\\(\\)?/g
const rePropName = RegExp(
//匹配没有点号和括号的字符串,'\\]'为转义']' 例如:'a'
'[^.[\\]]+' + '|' +
// Or match property names within brackets.
'\\[(?:' +
// 匹配一个非字符的表达式,例如 '[index]'
'([^"\'][^[]*)' + '|' +
// 匹配字符串,例如 '["a"]'
'(["\'])((?:(?!\\2)[^\\\\]|\\\\.)*?)\\2' +
')\\]' + '|' +
'(?=(?:\\.|\\[\\])(?:\\.|\\[\\]|$))'
, 'g')
function get(object, path, defaultValue) {
let result;
if (object == null) return result;
if (!Array.isArray(path)) {
//是否为key
const type = typeof path
if (type == 'number' || type == 'boolean' || path == null || toString.call(path) == '[object Symbol]' || reIsPlainProp.test(path) || !reIsDeepProp.test(path)) {
path = [path];
} else {
//字符串转化为数组
const tmp = []
//第一个字符是'.'
if (path.charCodeAt(0) === '.') {
tmp.push('')
}
path.replace(rePropName, (match, expression, quote, subString) => {
let key = match
if (quote) {
key = subString.replace(reEscapeChar, '$1')
} else if (expression) {
key = expression.trim()
}
tmp.push(key)
})
path = tmp;
}
}
//转化为数组后的通用部分
let index = 0
const length = path.length
while (object != null && index < length) {
let value = path[index++];
object = object[value]
}
result = (index && index == length) ? object : undefined
return result === undefined ? defaultValue : result
} |
ES2020 可以链式 & 空值合并运算data?.b?.[4] ?? 20 |
path参数还有数组的可能 |
newPath.replace(/[(\w)+]/g, ".$1"); 这里是否应该是 newPath.replace(/[(\d+)]/g, ".$1"); |
function get(target, path, defaultValue) {
let keys = path.replace(/\[(\d+)\]/g, ".$1").split(".");
return keys.reduce((t, key) => (t || {})[key], target) ?? defaultValue;
} |
这道题还是非常不错的,可以锻炼正则和错误处理
function get(obj, path) {
const keys = path.split('.');
let res = obj;
for(let i = 0; i < keys.length; i++) {
const reg = /^(\w+)\[(\d+)\]$/;
const key = keys[i];
if(reg.test(key)) { // 数组索引
const tmp = key.match(reg);
const k = tmp[1]; // 键
const i = Number(tmp[2]); // 索引
let isError = false;
try {
res = res[k][i];
} catch (error) {
isError = true;
} finally {
if(res == undefined) isError = true;
}
if(isError) break;
}else { // 对象键
res = res[key];
if(res == undefined) return '';;
}
}
return res;
}
get({ 'a': [{ 'b': { 'c': 3 } }] }, 'a[0].b.c'); |
function lensProp(obj = {}, path = '') {
if (typeof obj !== 'object' || obj === null) {
obj = {}
}
let props = path.replace(/\[/g, '.').replace(/\]/g, '').split('.')
for (let i = 0; i < props.length; i++) {
if (typeof obj[props[i]] === 'undefined') {
return void 0;
} else {
// debugger
if (typeof obj[props[i]] === 'object' && obj !== null) {
obj = obj[props[i]]
} else {
return i === props.length - 1 ? obj[props[i]] : void 0;
}
}
}
return obj;
}
var obj6 = {
name: 'yzf',
children: [{
name: 'yy',
age: 1,
children: [
{
name: 'yyy',
age: 1,
children: []
}
]
}, {
name: 'yy1',
age: 8,
children: []
}],
other: {
year: 29
}
}
// console.log(lensProp(obj6, 'children.0.name'));
// console.log(lensProp(obj6, 'children[0].name')); |
const get = function (object, path, defaultValue = undefined) {
const paths = Array.isArray(path) ? path : path.replace(/\[(\d+)\]/g, ".$1").split(".");
let result = object;
for (const key of paths) {
result = Object(result)[key];
if (result === undefined) {
return defaultValue;
}
}
return result;
}; |
function get(obj, path, defaultValue){
let data = obj
path = path.replace(/\[(\d+)\]/ig, ".$1").split('.')
for(let key of path){
if(data[key]){
data = data[key]
}else{
return defaultValue
}
}
return data
}
// 测试用例
console.log(get({ a: null }, "a.b.c", 3)); // output: 3
console.log(get({ a: undefined }, "a", 3)); // output: 3
console.log(get({ a: null }, "a.b", 3)); // output: 3
console.log(get({ a: [{ b: 1 }] }, "a[0].b", 3)); // output: 1
console.log(get({ a: { b : { c: 2 } } }, "a.b.c", 3)); // output: 2 |
/**
* @description 仿lodash的get方法,优化属性嵌套地址狱
* @param { Object } source 要检测的源变量
* @param { String } path 变量路径
* @param { Any } dValue 如果未匹配到,则返回默认值
* @return { Any } 返回目标值或者默认值
*/
const validationValue = (source, path, dValue = undefined) => {
const paths = Object.prototype.toString.call(path) === '[object Array]'
? path
: path.replace(/\[(\d+)\]/g, '.$1').split('.');
for (let i of paths) {
source = source?.[i];
if (source === undefined) return dValue;
}
return source;
}
console.log(1111, validationValue({ a: null }, 'a.b.c', 3))
console.log(2222, validationValue({ a: undefined }, 'a', 3))
console.log(3333, validationValue({ a: null }, 'a.b', 3))
console.log(4444, validationValue({ a: [{ b: 1 }] }, 'a[0].b', 3))
console.log(5555, validationValue({ a: { b : { c: 2 } } }, 'a.b.c', 3))
console.log(6666, validationValue({ 'a': [{ 'b': { 'c': 3 } }] }, 'a[0].b.c', 8)) |
改进版本 function get (source, path, defaultValue = undefined) {
const paths = Array.isArray(path) ? path
: path.replace(/\[(\d+)\]/g, '.$1')
.split('.')
.filter(Boolean)
let result = source
for (const p of paths) {
result = Object(result)[p]
if (result == undefined) {
return defaultValue
}
}
return result
}
console.log(
get({ a: [{ b: 1 }] }, 'a[0].b', 3)
) // output: 1
console.log(
get({ a: [{ b: 1 }] }, '.a[0].b', 3)
) // output: 1
console.log(
get({ a: [{ b: 1 }] }, ['a',0,'b'], 3)
) // output: 1 |
function get(obj, path, defaults) {
try {
const val = path.split('.')
.map((item) => item.split(/\[(\d)\]/).filter(Boolean)).flat().reduce((acc, v) => acc[v], obj)
if (typeof defaults !== 'undefined' && val === undefined) {
return defaults
}
return val
} catch (_) {
return defaults
}
} |
function get(target: object, propStr: string, defaultValue: any = undefined) {
const props = propStr.split('.')
let t: any = target
for (const prop of props) {
if (t == null) break
t = t[prop]
}
if (t === undefined) return defaultValue
return t
} |
const getPath = (path) => { |
/**
* 实现lodash的_.get
* lodash官方文档
* Gets the value at path of object. If the resolved value is undefined,
* the defaultValue is returned in its place.
*
* Arguments
object (Object): The object to query.
path (Array|string): The path of the property to get.
[defaultValue] (*): The value returned for undefined resolved values.
* Returns
(*): Returns the resolved value.
* 官方示例
var object = { 'a': [{ 'b': { 'c': 3 } }] };
_.get(object, 'a[0].b.c');
// => 3
_.get(object, ['a', '0', 'b', 'c']);
// => 3
_.get(object, 'a.b.c', 'default');
// => 'default'
*/
class Lodash {
get(object, path, defaultValue = undefined) {
if (typeof object !== "object") {
throw new Error("first argument of get should be an object");
}
if (path instanceof Array) {
return this._keysReduce(object, path, defaultValue);
}
if (typeof path === "string") {
const keys = path.split(/[\.|\[|\]]/).filter((key) => Boolean(key));
return this._keysReduce(object, keys, defaultValue);
}
}
_keysReduce(object, path, defaultValue) {
return path.reduce((pre, cur) => {
if (typeof cur !== "string") {
throw new Error("path should be an Array of Strings");
}
if (pre === undefined) {
return defaultValue;
}
return pre[cur];
}, object);
}
}
const _ = new Lodash();
const object = { a: [{ b: { c: 3 } }] };
console.log(_.get(object, "a[0].b.c"));
console.log(_.get(object, ["a", "0", "b", "c"]));
console.log(_.get(object, "a.b.c", "default")); |
欢迎在下方发表您的优质见解
The text was updated successfully, but these errors were encountered: