Created
September 30, 2019 07:36
-
-
Save watert/4167c712e34c2f8f9fde43e311be21c6 to your computer and use it in GitHub Desktop.
nested object set and get // extract from lodash
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
path access: object util get/set with path access | |
get(obj, path) => value of obj at path: | |
expect(get({ a: 0 }, 'b[1].c')).toBe(null); | |
expect(get({ b: [0, { c: 'CCC' }] }, 'b[1].c')).toBe('CCC'); | |
set(obj, path, value) => new object updates object value at path | |
expect(set({a: 0}, 'b.c', 1)).toMatchObject({ a:0, b: {c: 1} }); | |
expect(set({a: 0}, 'b.c', 2, (v) => v * 10).b.c).toBe(20); | |
expect(set({ a: 0 }, 'b[1]', 1)).toMatchObject({ a:0, b: [undefined, 1] }); | |
expect(set({ a: [0] }, 'a[2]', 'VALUE')).toMatchObject({ a:[0, undefined, 'VALUE'] }); | |
expect(set({ a: [0] }, 'a[2].c', 'VALUE')).toMatchObject({ a:[0, undefined, {c: 'VALUE'}] }); | |
set(obj, path, updater) => new object updates object with updater at path | |
*/ | |
const reLeadingDot = /^\./; | |
const rePropName = /[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g; | |
const reEscapeChar = /\\(\\)?/g; | |
const isNaN = (i) => i !== i | |
const isArr = (a) => a instanceof Array | |
const isArrIndex = (key) => !isNaN(parseInt(key, 10)); | |
export function stringToPath(string) { | |
const result = []; | |
if (reLeadingDot.test(string)) { | |
result.push(''); | |
} | |
string.replace(rePropName, function(match, number, quote, string) { | |
result.push(quote ? string.replace(reEscapeChar, '$1') : (number || match)); | |
}); | |
return result; | |
}; | |
export function toPath(path) { | |
return typeof path === 'string' ? stringToPath(path) : [].concat(path); | |
} | |
export function get(obj, path) { | |
return stringToPath(path).reduce((memo, key) => { | |
if (memo == null || !(key in memo)) return null; | |
return memo[key]; | |
}, obj); | |
} | |
const isPositiveInteger = (n) => n >>> 0 === parseFloat(n); // 这个版本只支持最大到 4294967295( Math.pow(2,32) - 1) ) 的数值 | |
// function isPositiveInteger(n) { // 这个版本能支持到 Number.MAX_VALUE | |
// return 0 === n % (!isNaN(parseFloat(n)) && 0 <= ~~n); | |
// } | |
// 详细文档见 SO https://stackoverflow.com/questions/10834796/validate-that-a-string-is-a-positive-integer | |
function _set(obj, pathArr, value, customizer = r => r) { | |
if (pathArr.length === 0) { | |
return typeof value === 'function' ? value(obj) : value; | |
} | |
const firstKey = pathArr[0]; | |
if (typeof obj !== 'object') { | |
obj = isPositiveInteger(firstKey) ? [] : {}; | |
} | |
const val = _set(obj[firstKey], pathArr.slice(1), value, customizer); | |
let next; | |
if (Array.isArray(obj)) { | |
next = [].concat(obj); | |
next[firstKey] = val; | |
} else { | |
next = { ...obj, [firstKey]: val }; | |
} | |
if (pathArr.length === 1) { | |
return customizer(next, firstKey, obj); | |
} | |
return next; | |
} | |
export default function set(obj, path, value, customizer) { | |
return _set(obj, toPath(path), value, customizer); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment