import isFunction from 'lodash-es/isFunction.js'
import size from 'lodash-es/size.js'
import get from 'lodash-es/get.js'
import range from 'lodash-es/range.js'
import cloneDeep from 'lodash-es/cloneDeep.js'
import isarr from './isarr.mjs'
import isobj from './isobj.mjs'
import isbol from './isbol.mjs'
//performance for traverseObj and runObj
// let data = {
// a: 123,
// b: 145.67,
// c: 'test中文1',
// d: true,
// e: function() {},
// f: [11, 'xyz', false, new Uint8Array([166, 197, 215])],
// g: {
// ga: 223,
// gb: 245.67,
// gc: 'test中文2',
// gd: new Uint8Array([66, 97, 115]),
// },
// h: Symbol('foo'),
// [Symbol('i-sym-key')]: 'i-sym-value',
// }
// if (true) {
// let start = process.hrtime()
// for (let i = 1; i <= 1000000; i++) {
// let r1 = traverseObj(data, (v, k) => {
// return v
// })
// }
// let diff = process.hrtime(start)
// let elapsedtime = diff[0] + diff[1] / 1e9
// console.log('traverseObj elapsedtime', elapsedtime)
// // traverseObj elapsedtime 4.2645716
// // traverseObj elapsedtime 4.2282303
// // traverseObj elapsedtime 4.2010933
// }
// if (true) {
// let start = process.hrtime()
// for (let i = 1; i <= 1000000; i++) {
// let r1 = runObj(data, (v, k) => {
// return v
// })
// }
// let diff = process.hrtime(start)
// let elapsedtime = diff[0] + diff[1] / 1e9
// console.log('runObj elapsedtime', elapsedtime)
// // runObj elapsedtime 10.2841799
// // runObj elapsedtime 10.8524164
// // runObj elapsedtime 10.8240743
// }
function traverseObj(data, f, keepSymbols = false) {
let nowKey = []
//check
if (!isarr(data) && !isobj(data)) {
data = f(data, null, [])
return data
}
function getKeys(obj) {
if (isarr(obj)) {
//return keys(obj)
return range(size(obj))
}
if (isobj(obj)) {
let v1 = Object.getOwnPropertyNames(obj)
// console.log('v1', v1)
let v2 = null
if (keepSymbols) {
v2 = Object.getOwnPropertySymbols(obj)
}
// console.log('v2', v2)
if (!isarr(v1)) {
v1 = []
}
if (!isarr(v2)) {
v2 = []
}
return [...v1, ...v2]
}
return obj
}
function traverse(vs, _k) {
//console.log('vs', vs)
if (isarr(vs)) {
let ks = getKeys(vs)
return ks.map((k) => {
let v = vs[k]
//console.log('map', v, k)
let t = cloneDeep(nowKey)
nowKey.push(k)
v = f(v, k, t)
v = traverse(v, k)
nowKey.pop()
return v
})
}
else if (isobj(vs)) {
let ks = getKeys(vs)
let o = {}
ks.map((k) => {
let v = vs[k]
//console.log('map', v, k)
let t = cloneDeep(nowKey)
nowKey.push(k)
v = f(v, k, t)
v = traverse(v, k)
nowKey.pop()
o[k] = v
})
return o
}
else {
return vs //f(vs, _k, cloneDeep(slice(nowKey, 1)))
}
}
return traverse(data, f)
}
// function runObj(data, hookFun) {
// let p = {}
// let nowKey = ['pnode']
// //列舉pArray內元素
// function pArray({ value }) {
// let n = size(value)
// for (let k = 0; k < n; k++) {
// pSelf({
// key: k,
// value: value[k],
// })
// }
// }
// //列舉pObject內元素
// function pObject({ value }) {
// let kks = Object.keys(value)
// let n = kks.length
// for (let i = 0; i < n; i++) {
// let k = kks[i]
// pSelf({
// key: k,
// value: value[k],
// })
// }
// }
// //取得值的型別
// function getType(value) {
// if (isarr(value)) {
// return 'array'
// }
// else if (isobj(value)) {
// return 'object'
// }
// else if (isNumber(value) || isString(value) || isBoolean(value)) {
// return 'common'
// }
// else if (isab(value) || isu8arr(value) || isu16arr(value)) {
// return 'binary'
// }
// else {
// return 'special'
// }
// }
// //遞迴處理自己元素
// function pSelf({ key, value }) {
// //hookFun
// let nk = slice(nowKey, 1)
// value = hookFun(value, key, nk)
// //getType
// let t = getType(value)
// //deal
// if (t === 'array') {
// nowKey.push(key)
// pArray({ value })
// nowKey.pop()
// }
// else if (t === 'object') {
// nowKey.push(key)
// pObject({ value })
// nowKey.pop()
// }
// else if (t === 'common') {
// set(p, [...nowKey, key], value)
// }
// else if (t === 'binary') {
// set(p, [...nowKey, key], value)
// }
// else {
// //special
// set(p, [...nowKey, key], value)
// }
// }
// //top node
// if (isarr(data)) {
// if (isarr0(data)) {
// return data
// }
// pArray({
// value: data,
// })
// }
// else if (isobj(data)) {
// if (isobj0(data)) {
// return data
// }
// pObject({
// value: data,
// })
// }
// else {
// let value = data
// set(p, nowKey, value)
// }
// return p.pnode
// }
/**
* 遍歷物件並回傳複製物件,類似JSON.stringify,但不會序列化成為字串
*
* Unit Test: {@link https://github.com/yuda-lyu/wsemi/blob/master/test/treeObj.test.mjs Github}
* @memberOf wsemi
* @param {*} data 輸入任意資料
* @param {Function} [hookFun=null] 輸入攔截處理函數,預設null
* @param {Object} [opt={}] 輸入設定物件,預設{}
* @param {Boolean} [opt.force=false] 輸入是否允許鍵值為Symbol類型布林值,預設false
* @returns {*} 回傳複製後的任意資料
* @example
*
* let r
* let data = {
* a: 123,
* b: 145.67,
* c: 'test中文1',
* d: true,
* e: function() {},
* f: [11, 'xyz', false, new Uint8Array([166, 197, 215])],
* g: {
* ga: 223,
* gb: 245.67,
* gc: 'test中文2',
* gd: new Uint8Array([66, 97, 115]),
* },
* h: Symbol('foo'),
* [Symbol('i-sym-key')]: 'i-sym-value',
* }
*
* r = treeObj(data, (value, key, nk) => {
* console.log('=>', value, key, nk)
* return value
* })
* console.log('force: false', r)
* // => 123 a []
* // => 145.67 b []
* // => test中文1 c []
* // => true d []
* // => [Function: e] e []
* // => [ 11, 'xyz', false, Uint8Array(3) [ 166, 197, 215 ] ] f []
* // => 11 0 [ 'f' ]
* // => xyz 1 [ 'f' ]
* // => false 2 [ 'f' ]
* // => Uint8Array(3) [ 166, 197, 215 ] 3 [ 'f' ]
* // => {
* // ga: 223,
* // gb: 245.67,
* // gc: 'test中文2',
* // gd: Uint8Array(3) [ 66, 97, 115 ]
* // } g []
* // => 223 ga [ 'g' ]
* // => 245.67 gb [ 'g' ]
* // => test中文2 gc [ 'g' ]
* // => Uint8Array(3) [ 66, 97, 115 ] gd [ 'g' ]
* // => Symbol(foo) h []
* // force: false {
* // a: 123,
* // b: 145.67,
* // c: 'test中文1',
* // d: true,
* // e: [Function: e],
* // f: [ 11, 'xyz', false, Uint8Array(3) [ 166, 197, 215 ] ],
* // g: {
* // ga: 223,
* // gb: 245.67,
* // gc: 'test中文2',
* // gd: Uint8Array(3) [ 66, 97, 115 ]
* // },
* // h: Symbol(foo)
* // }
*
* r = treeObj(data, (value, key, nk) => {
* console.log('=>', value, key, nk)
* return value
* }, { force: true })
* console.log('force: true', r)
* // => 123 a []
* // => 145.67 b []
* // => test中文1 c []
* // => true d []
* // => [Function: e] e []
* // => [ 11, 'xyz', false, Uint8Array(3) [ 166, 197, 215 ] ] f []
* // => 11 0 [ 'f' ]
* // => xyz 1 [ 'f' ]
* // => false 2 [ 'f' ]
* // => Uint8Array(3) [ 166, 197, 215 ] 3 [ 'f' ]
* // => {
* // ga: 223,
* // gb: 245.67,
* // gc: 'test中文2',
* // gd: Uint8Array(3) [ 66, 97, 115 ]
* // } g []
* // => 223 ga [ 'g' ]
* // => 245.67 gb [ 'g' ]
* // => test中文2 gc [ 'g' ]
* // => Uint8Array(3) [ 66, 97, 115 ] gd [ 'g' ]
* // => Symbol(foo) h []
* // => i-sym-value Symbol(i-sym-key) []
* // force: true {
* // a: 123,
* // b: 145.67,
* // c: 'test中文1',
* // d: true,
* // e: [Function: e],
* // f: [ 11, 'xyz', false, Uint8Array(3) [ 166, 197, 215 ] ],
* // g: {
* // ga: 223,
* // gb: 245.67,
* // gc: 'test中文2',
* // gd: Uint8Array(3) [ 66, 97, 115 ]
* // },
* // h: Symbol(foo),
* // [Symbol(i-sym-key)]: 'i-sym-value'
* // }
*
*/
function treeObj(data, hookFun = null, opt = {}) {
//hookFun
if (!isFunction(hookFun)) {
hookFun = (value) => {
return value
}
}
//force
let force = get(opt, 'force', false)
if (!isbol(force)) {
force = false
}
//call
return traverseObj(data, hookFun, force)
}
export default treeObj