如何实现类似 lodash.get 函数
更多描述 使用
get
函数可避免长链的 key 时获取不到属性而出现问题,此时进行异常避免时及其服务,如o.a && o.a.b && o.a.b.c && o.a.b.c.d
实现类似lodash.get,有以下测试用例:
const object = { a: [{ b: { c: 3 } }] };
//=> 3
get(object, "a[0].b.c");
//=> 3
get(object, 'a[0]["b"]["c"]');
//=> 10086
get(object, "a[100].b.c", 10086);
问题追问:
1. 如何使用 ts 写法来实现 lodash.get 函数?
Issue 欢迎在 Gtihub Issue 中回答此问题: Issue 199
Author 回答者: miaooow
function lodashGet(obj,exps){ if(typeof exps !== ‘string’) return obj if(typeof obj !== ‘object’) return obj let res = obj const arr = exps.split(’.’) for(let i=0;i<arr.length;i++){ const exp = arr[i] if(res[exp]){ res = res[exp] } else{ return undefined } } return res }
var obj = {test:{arr:[{name:1}]}}
lodashGet(obj,‘test.arr.0.name’)
Author 回答者: shfshanyue
代码见 如何实现类似 lodash.get 函数 - codepen
function get(source, path, defaultValue = undefined) {
// a[3].b -> a.3.b -> [a, 3, b]
const paths = path
.replace(/\[(\w+)\]/g, ".$1")
.replace(/\["(\w+)"\]/g, ".$1")
.replace(/\['(\w+)'\]/g, ".$1")
.split(".");
let result = source;
for (const p of paths) {
result = result?.[p];
}
return result === undefined ? defaultValue : result;
}
const object = { a: [{ b: { c: 3 } }] };
const result = _.get(object, "a[0].b.c", 1);
Author 回答者: haotie1990
function getValue(context, path, defaultValue) {
if (
Object.prototype.toString.call(context) !== "[object Object]" &&
Object.prototype.toString.call(context) !== "[object Array]"
) {
return context;
}
let paths = [];
if (Array.isArray(path)) {
paths = [...path];
} else if (Object.prototype.toString.call(path) === "[object String]") {
paths = path
.replace(/\[/g, ".")
.replace(/\]/g, "")
.split(".")
.filter(Boolean);
} else {
paths = [String(path)];
}
let result = undefined;
for (let i = 0; i < paths.length; i++) {
const key = paths[i];
result = result ? result[key] : context[key];
if (result !== null && typeof result !== "undefined") {
continue;
}
return defaultValue || undefined;
}
return result;
}
Author 回答者: heretic-G
// 其实原本是按照lodash实现的 但是这里有个差异是如果属性存在就返回其实没有把目标元素是`undefined`的时候设置回default
function get(arm, params = "", defaultVal) {
if (typeof params !== "string" && !Array.isArray(params)) {
throw new Error(`${params} is not string or array`);
}
if (!Array.isArray(params)) {
params = params.split(/\].|[\[.]/);
}
for (let i = 0; i < params.length; i++) {
if (Object.prototype.hasOwnProperty.call(arm, params[i])) {
arm = arm[params[i]];
} else {
return defaultVal;
}
}
return arm;
}
function get(obj, keyStr, defVal = undefined) {
let matchArr = Array.from(
keyStr.matchAll(/(\[).*?(\])|(?<=\.).*?(?=\.)|(?<=\.).*?$/g),
);
let val = obj;
for (let i = 0; i < matchArr.length; i++) {
if (
(typeof val === "object" && val !== null) ||
typeof val === "function"
) {
let key = matchArr[i][0];
if (key[0] === "[") {
key = key.slice(1, key.length - 1);
}
val = obj[key];
} else {
return defVal;
}
}
if (val === undefined) {
return defVal;
} else {
return val;
}
}
type strToPoint<S> = S extends `${infer F}["${infer M}`
? strToPoint<`${F}.${M}`>
: S extends `${infer F}"]${infer M}`
? strToPoint<`${F}${M}`>
: S extends `${infer F}['${infer M}`
? strToPoint<`${F}.${M}`>
: S extends `${infer F}']${infer M}`
? strToPoint<`${F}${M}`>
: S extends `${infer F}[${infer M}`
? strToPoint<`${F}.${M}`>
: S extends `${infer F}]${infer M}`
? strToPoint<`${F}${M}`>
: S;
type strPointToArr<
S,
A extends string[] = [],
> = S extends `${infer F}.${infer M}`
? strPointToArr<M, [...A, F]>
: S extends ""
? A
: [...A, S];
type getReturnType<
O extends unknown,
K extends string[],
D extends unknown = undefined,
> = K extends []
? O extends undefined
? D
: O
: O extends Record<string, any>
? getReturnType<
K[0] extends keyof O ? O[K[0]] : undefined,
K extends [first: infer F, ...args: infer L] ? L : [],
D
>
: D;
let obj = {
a: [
1,
"lisi",
{
b: {
c: 4,
},
f: {
g: "wangwu",
},
},
],
} as const;
type get<
O extends Record<string, any>,
K extends string,
Def extends unknown = undefined,
> = (
obj: O,
keyStr: K,
defVal: Def,
) => getReturnType<O, strPointToArr<strToPoint<K>>, Def>;
type zz = get<typeof obj, "a[2][b].c", "123">;
type zzz = get<typeof obj, "d[e]", "defaultVal">;
Author 回答者: hwb2017
const lodashGet = (
object: { [key: string]: any },
path: Array<string> | string,
defaultValue?: any,
): any => {
let result: any;
const findArrayPath = (path: Array<string>): any => {
if (path.length === 0) {
return (result = defaultValue);
}
result = object;
for (const p of path) {
if (p in result) {
result = result[p];
} else {
result = defaultValue;
break;
}
}
return result;
};
if (Array.isArray(path)) {
result = findArrayPath(path);
} else {
path.replace;
let normalizedPath = path.replace(/\.|\[|\]/g, " ").split(/\s+/);
result = findArrayPath(normalizedPath);
}
return result;
};
const object = { a: [{ b: { c: 3 } }] };
console.log(lodashGet(object, "a[0].b.c"));
console.log(lodashGet(object, ["a", "0", "b", "c"]));
console.log(lodashGet(object, "a.b.c", "default"));
Author 回答者: yazhouio
function get(obj, keys, defaultValue) {
let tempObj = obj;
let arr = [];
if (typeof keys === "string") {
let key = "";
let index = 0;
while (index < keys.length) {
const k = keys[index];
if (["[", "'", '"', ".", "]"].includes(k)) {
if (key.length) {
arr.push(key);
}
key = "";
} else {
key = key + k;
}
index = index + 1;
}
key && arr.push(key);
} else {
arr = keys;
}
while (arr.length) {
tempObj = tempObj[arr.shift()];
if (tempObj === undefined || tempObj === null) {
return defaultValue;
}
}
return tempObj;
}
Author 回答者: 4may-mcx
function get(obj, oriPath, defaultVal) {
const paths = oriPath.split(".");
const keys = [];
for (const path of paths) {
keys.push(...pathHandler(path));
}
let res = obj;
for (const key of keys) {
if (res[key] === undefined || res[key] === null) return defaultVal;
res = res[key];
}
return res;
}
// 将 path 处理为能用的 keys
function pathHandler(path) {
const res = [];
path = path.split("");
let left = 0;
let right = 0;
let str = "";
while (left < path.length && right < path.length) {
if (path[left] === "[") {
right = left + 1;
while (path[right] !== "]") {
right++;
}
temp = path.slice(left + 1, right).join("");
left = right + 1;
temp && res.push(temp);
} else {
str += path[left++];
}
}
res.unshift(str);
return res.map((item) => {
if (item[0] >= "0" && item[0] <= "9") {
// 转化为整数
return parseInt(item);
}
if (item.indexOf('"') !== -1 || item.indexOf("'") !== -1) {
// 去除两边的引号
return item.slice(1, item.length - 1);
}
return item;
});
}