import get from 'lodash-es/get.js'
import set from 'lodash-es/set.js'
import each from 'lodash-es/each.js'
import map from 'lodash-es/map.js'
import size from 'lodash-es/size.js'
import max from 'lodash-es/max.js'
import values from 'lodash-es/values.js'
import groupBy from 'lodash-es/groupBy.js'
import join from 'lodash-es/join.js'
import find from 'lodash-es/find.js'
import sortBy from 'lodash-es/sortBy.js'
import cloneDeep from 'lodash-es/cloneDeep.js'
import sep from './sep.mjs'
import isestr from './isestr.mjs'
import isarr from './isarr.mjs'
import isearr from './isearr.mjs'
import iseobj from './iseobj.mjs'
import isfun from './isfun.mjs'
import haskey from './haskey.mjs'
/**
* 轉換檔案路徑陣列成為樹狀物件與關聯資料物件
*
* Unit Test: {@link https://github.com/yuda-lyu/wsemi/blob/master/test/filepathToTree.test.mjs Github}
* @memberOf wsemi
* @param {Array} fps 輸入項目陣列,每個項目需為物件,並提供path與type兩鍵值,其中type需為'folder'或'file'
* @param {Object} [opt={}] 輸入設定物件,預設{}
* @param {String} [delimiter='❯'] 輸入路徑分隔字串,預設'❯'
* @param {String} [bindRoot='root'] 輸入臨時封裝用根目錄字串,預設'root'
* @param {Function} [soryItems=null] 輸入排序產生樹狀物件函數,預設null
* @returns {Object} 回傳結果物件,其內treeItems為資料夾與檔案之樹狀物件,treeItemsFolder為僅資料夾之樹狀物件,kpPath為各項目id之取樹狀物件路徑物件,fpsNormalize為重新正規化之檔案路徑陣列
* @example
*
* let fps1 = [{ 'type': 'folder', 'path': '/aaa' }, { 'type': 'file', 'path': '/aaa/bbb/z1.txt' }]
* let r1 = filepathToTree(fps1)
* //fs.writeFileSync('r1.json', JSON.stringify(r1), 'utf8')
* console.log(r1)
* console.log('\n\n')
* // => {
* // treeItems: [
* // {
* // _indOri: null,
* // _indNormalize: null,
* // ns: 1,
* // ts: [Array],
* // pathInfors: [Array],
* // _type: 'folder',
* // type: 'array',
* // numOfChilren: -1,
* // id: 'root',
* // parentId: '',
* // text: 'root',
* // children: [Array],
* // data: null
* // }
* // ],
* // treeItemsFolder: [
* // {
* // _indOri: null,
* // _indNormalize: null,
* // ns: 1,
* // ts: [Array],
* // pathInfors: [Array],
* // _type: 'folder',
* // type: 'array',
* // numOfChilren: -1,
* // id: 'root',
* // parentId: '',
* // text: 'root',
* // children: [Array],
* // data: null
* // }
* // ],
* // kpPath: {
* // root: '0',
* // 'root❯aaa': '0.children.0',
* // 'root❯aaa❯bbb': '0.children.0.children.0',
* // 'root❯aaa❯bbb❯z1.txt': '0.children.0.children.0.children.0'
* // },
* // fpsNormalize: [
* // { type: 'folder', path: '/root/aaa' },
* // { type: 'folder', path: '/root/aaa/bbb' },
* // { type: 'file', path: '/root/aaa/bbb/z1.txt' }
* // ]
* // }
*
* let fps2 = [{ 'type': 'folder', 'path': '/aaa' }, { 'type': 'file', 'path': '/aaa/bbb/z1.txt' }]
* let r2 = filepathToTree(fps2, { delimiter: '>' })
* //fs.writeFileSync('r2.json', JSON.stringify(r2), 'utf8')
* console.log(r2)
* console.log('\n\n')
* // => {
* // treeItems: [
* // {
* // _indOri: null,
* // _indNormalize: null,
* // ns: 1,
* // ts: [Array],
* // pathInfors: [Array],
* // _type: 'folder',
* // type: 'array',
* // numOfChilren: -1,
* // id: 'root',
* // parentId: '',
* // text: 'root',
* // children: [Array],
* // data: null
* // }
* // ],
* // treeItemsFolder: [
* // {
* // _indOri: null,
* // _indNormalize: null,
* // ns: 1,
* // ts: [Array],
* // pathInfors: [Array],
* // _type: 'folder',
* // type: 'array',
* // numOfChilren: -1,
* // id: 'root',
* // parentId: '',
* // text: 'root',
* // children: [Array],
* // data: null
* // }
* // ],
* // kpPath: {
* // root: '0',
* // 'root>aaa': '0.children.0',
* // 'root>aaa>bbb': '0.children.0.children.0',
* // 'root>aaa>bbb>z1.txt': '0.children.0.children.0.children.0'
* // },
* // fpsNormalize: [
* // { type: 'folder', path: '/root/aaa' },
* // { type: 'folder', path: '/root/aaa/bbb' },
* // { type: 'file', path: '/root/aaa/bbb/z1.txt' }
* // ]
* // }
*
* let fps3 = [{ 'type': 'folder', 'path': '/aaa' }, { 'type': 'file', 'path': '/aaa/bbb/z1.txt' }]
* let r3 = filepathToTree(fps3, { bindRoot: '本機' })
* //fs.writeFileSync('r3.json', JSON.stringify(r3), 'utf8')
* console.log(r3)
* console.log('\n\n')
* // => {
* // treeItems: [
* // {
* // _indOri: null,
* // _indNormalize: null,
* // ns: 1,
* // ts: [Array],
* // pathInfors: [Array],
* // _type: 'folder',
* // type: 'array',
* // numOfChilren: -1,
* // id: '本機',
* // parentId: '',
* // text: '本機',
* // children: [Array],
* // data: null
* // }
* // ],
* // treeItemsFolder: [
* // {
* // _indOri: null,
* // _indNormalize: null,
* // ns: 1,
* // ts: [Array],
* // pathInfors: [Array],
* // _type: 'folder',
* // type: 'array',
* // numOfChilren: -1,
* // id: '本機',
* // parentId: '',
* // text: '本機',
* // children: [Array],
* // data: null
* // }
* // ],
* // kpPath: {
* // '本機': '0',
* // '本機❯aaa': '0.children.0',
* // '本機❯aaa❯bbb': '0.children.0.children.0',
* // '本機❯aaa❯bbb❯z1.txt': '0.children.0.children.0.children.0'
* // },
* // fpsNormalize: [
* // { type: 'folder', path: '/本機/aaa' },
* // { type: 'folder', path: '/本機/aaa/bbb' },
* // { type: 'file', path: '/本機/aaa/bbb/z1.txt' }
* // ]
* // }
*
* let fps4 = [{ 'type': 'folder', 'path': '/aaa' }, { 'type': 'file', 'path': '/aaa1.txt' }, { 'type': 'file', 'path': '/aaa2.txt' }, { 'type': 'folder', 'path': '/aaa/aaabbb' }, { 'type': 'file', 'path': '/aaa/aaabbb.txt' }, { 'type': 'file', 'path': '/aaa/aaabbb/aaabbbccc.txt' }, { 'type': 'file', 'path': '/aaa/aaabbb/abc/abcd/abcde.txt' }, { 'type': 'file', 'path': '/aaa/aaabbb/abc/abcde/abcdef1.txt' }, { 'type': 'file', 'path': '/aaa/aaabbb/abc/abcde/abcdef2.txt' }, { 'type': 'file', 'path': '/aaa/aaabbb/abc/abcde/abcdef3 aaa bbb ccc dddddd eeeeeee fffffffffff ggggggggggggg.txt' }, { 'type': 'file', 'path': '/aaa/aaabbb/abc/abcde/abcdefg01.txt' }, { 'type': 'file', 'path': '/aaa/aaabbb/abc/abcde/abcdefg02.txt' }, { 'type': 'file', 'path': '/aaa/aaabbb/abc/abcde/abcdefg03.txt' }, { 'type': 'file', 'path': '/aaa/aaabbb/abc/abcde/abcdefg04.txt' }, { 'type': 'file', 'path': '/aaa/aaabbb/abc/abcde/abcdefg05.txt' }, { 'type': 'file', 'path': '/aaa/aaabbb/abc/abcde/abcdefg06.txt' }, { 'type': 'file', 'path': '/aaa/aaabbb/abc/abcde/abcdefg07.txt' }, { 'type': 'file', 'path': '/aaa/aaabbb/abc/abcde/abcdefg08.txt' }, { 'type': 'file', 'path': '/aaa/aaabbb/abc/abcde/abcdefg09.txt' }, { 'type': 'file', 'path': '/aaa/aaabbb/abc/abcde/abcdefg10.txt' }, { 'type': 'file', 'path': '/aaa/aaabbb/abc/abcde/abcdefg11.txt' }, { 'type': 'file', 'path': '/aaa/aaabbb/abc/abcde/abcdefg12.txt' }, { 'type': 'file', 'path': '/aaa/aaabbb/abc/abcde/abcdefg13.txt' }, { 'type': 'file', 'path': '/aaa/aaabbb/abc/abcde/abcdefg14.txt' }, { 'type': 'file', 'path': '/aaa/aaabbb/abc/abcde/abcdefg15.txt' }, { 'type': 'file', 'path': '/aaa/aaabbb/abc/abcde/abcdefg16.txt' }, { 'type': 'file', 'path': '/aaa/aaabbb/abc/abcde/abcdefg17.txt' }, { 'type': 'file', 'path': '/aaa/aaabbb/abc/abcde/abcdefg18.txt' }, { 'type': 'file', 'path': '/aaa/aaabbb/abc/abcde/abcdefg19.txt' }, { 'type': 'file', 'path': '/aaa/aaabbb/abc/abcde/abcdefg20.txt' }, { 'type': 'file', 'path': '/aaa/aaabbb/abc/abcd1.txt' }, { 'type': 'file', 'path': '/aaa/aaabbb/abc/abcd2.txt' }, { 'type': 'folder', 'path': '/bbb' }, { 'type': 'file', 'path': '/ccc/cccddd/cccdddeee.txt' }, { 'type': 'folder', 'path': '/eee' }, { 'type': 'folder', 'path': '/eee/eeefff1' }, { 'type': 'folder', 'path': '/eee/eeefff2' }, { 'type': 'folder', 'path': '/ggg/' }, { 'type': 'folder', 'path': 'c:\\\\hhh' }, { 'type': 'folder', 'path': '/aaaa/bbbbbb cccccccccccc ddd dd ddd ddd ddd eeeeeeeeeeee ffff' }, { 'type': 'file', 'path': '/aaaa/bbbbbb cccccccccccc ddd dd ddd ddd ddd eeeeeeeeeeee ffff/gfedcba.txt' }, { 'type': 'file', 'path': '/aaaa/bbbbbb cccccccccccc ddd dd ddd ddd ddd eeeeeeeeeeee ffff/ggg/hhh.txt' }, { 'type': 'folder', 'path': 'd:\\\\中文路徑1' }, { 'type': 'folder', 'path': '/中文路徑2' }, { 'type': 'file', 'path': '/中文路徑2/aaa/aaabbb/abc/測試.txt' }]
* let r4 = filepathToTree(fps4)
* //fs.writeFileSync('r4.json', JSON.stringify(r4), 'utf8')
* console.log(r4)
* console.log('\n\n')
* // => {
* // treeItems: [
* // {
* // _indOri: null,
* // _indNormalize: null,
* // ns: 1,
* // ts: [Array],
* // pathInfors: [Array],
* // _type: 'folder',
* // type: 'array',
* // numOfChilren: -1,
* // id: 'root',
* // parentId: '',
* // text: 'root',
* // children: [Array],
* // data: null
* // }
* // ],
* // treeItemsFolder: [
* // {
* // _indOri: null,
* // _indNormalize: null,
* // ns: 1,
* // ts: [Array],
* // pathInfors: [Array],
* // _type: 'folder',
* // type: 'array',
* // numOfChilren: -1,
* // id: 'root',
* // parentId: '',
* // text: 'root',
* // children: [Array],
* // data: null
* // }
* // ],
* // kpPath: {
* // root: '0',
* // 'root❯aaa': '0.children.0',
* // 'root❯aaaa': '0.children.1',
* // 'root❯bbb': '0.children.2',
* // 'root❯c:': '0.children.3',
* // 'root❯ccc': '0.children.4',
* // 'root❯d:': '0.children.5',
* // 'root❯eee': '0.children.6',
* // 'root❯ggg': '0.children.7',
* // 'root❯中文路徑2': '0.children.8',
* // 'root❯aaaa❯bbbbbb cccccccccccc ddd dd ddd ddd ddd eeeeeeeeeeee ffff': '0.children.1.children.0',
* // 'root❯aaa❯aaabbb': '0.children.0.children.0',
* // 'root❯c:❯hhh': '0.children.3.children.0',
* // 'root❯ccc❯cccddd': '0.children.4.children.0',
* // 'root❯d:❯中文路徑1': '0.children.5.children.0',
* // 'root❯eee❯eeefff1': '0.children.6.children.0',
* // 'root❯eee❯eeefff2': '0.children.6.children.1',
* // 'root❯中文路徑2❯aaa': '0.children.8.children.0',
* // 'root❯aaaa❯bbbbbb cccccccccccc ddd dd ddd ddd ddd eeeeeeeeeeee ffff❯ggg': '0.children.1.children.0.children.0',
* // 'root❯aaa❯aaabbb❯abc': '0.children.0.children.0.children.0',
* // 'root❯中文路徑2❯aaa❯aaabbb': '0.children.8.children.0.children.0',
* // 'root❯aaa❯aaabbb❯abc❯abcd': '0.children.0.children.0.children.0.children.0',
* // 'root❯aaa❯aaabbb❯abc❯abcde': '0.children.0.children.0.children.0.children.1',
* // 'root❯中文路徑2❯aaa❯aaabbb❯abc': '0.children.8.children.0.children.0.children.0',
* // 'root❯aaa1.txt': '0.children.9',
* // 'root❯aaa2.txt': '0.children.10',
* // 'root❯aaa❯aaabbb.txt': '0.children.0.children.1',
* // 'root❯aaaa❯bbbbbb cccccccccccc ddd dd ddd ddd ddd eeeeeeeeeeee ffff❯gfedcba.txt': '0.children.1.children.0.children.1',
* // 'root❯aaa❯aaabbb❯aaabbbccc.txt': '0.children.0.children.0.children.1',
* // 'root❯ccc❯cccddd❯cccdddeee.txt': '0.children.4.children.0.children.0',
* // 'root❯aaaa❯bbbbbb cccccccccccc ddd dd ddd ddd ddd eeeeeeeeeeee ffff❯ggg❯hhh.txt': '0.children.1.children.0.children.0.children.0',
* // 'root❯aaa❯aaabbb❯abc❯abcd1.txt': '0.children.0.children.0.children.0.children.2',
* // 'root❯aaa❯aaabbb❯abc❯abcd2.txt': '0.children.0.children.0.children.0.children.3',
* // 'root❯aaa❯aaabbb❯abc❯abcde❯abcdef1.txt': '0.children.0.children.0.children.0.children.1.children.0',
* // 'root❯aaa❯aaabbb❯abc❯abcde❯abcdef2.txt': '0.children.0.children.0.children.0.children.1.children.1',
* // 'root❯aaa❯aaabbb❯abc❯abcde❯abcdef3 aaa bbb ccc dddddd eeeeeee fffffffffff ggggggggggggg.txt': '0.children.0.children.0.children.0.children.1.children.2',
* // 'root❯aaa❯aaabbb❯abc❯abcde❯abcdefg01.txt': '0.children.0.children.0.children.0.children.1.children.3',
* // 'root❯aaa❯aaabbb❯abc❯abcde❯abcdefg02.txt': '0.children.0.children.0.children.0.children.1.children.4',
* // 'root❯aaa❯aaabbb❯abc❯abcde❯abcdefg03.txt': '0.children.0.children.0.children.0.children.1.children.5',
* // 'root❯aaa❯aaabbb❯abc❯abcde❯abcdefg04.txt': '0.children.0.children.0.children.0.children.1.children.6',
* // 'root❯aaa❯aaabbb❯abc❯abcde❯abcdefg05.txt': '0.children.0.children.0.children.0.children.1.children.7',
* // 'root❯aaa❯aaabbb❯abc❯abcde❯abcdefg06.txt': '0.children.0.children.0.children.0.children.1.children.8',
* // 'root❯aaa❯aaabbb❯abc❯abcde❯abcdefg07.txt': '0.children.0.children.0.children.0.children.1.children.9',
* // 'root❯aaa❯aaabbb❯abc❯abcde❯abcdefg08.txt': '0.children.0.children.0.children.0.children.1.children.10',
* // 'root❯aaa❯aaabbb❯abc❯abcde❯abcdefg09.txt': '0.children.0.children.0.children.0.children.1.children.11',
* // 'root❯aaa❯aaabbb❯abc❯abcde❯abcdefg10.txt': '0.children.0.children.0.children.0.children.1.children.12',
* // 'root❯aaa❯aaabbb❯abc❯abcde❯abcdefg11.txt': '0.children.0.children.0.children.0.children.1.children.13',
* // 'root❯aaa❯aaabbb❯abc❯abcde❯abcdefg12.txt': '0.children.0.children.0.children.0.children.1.children.14',
* // 'root❯aaa❯aaabbb❯abc❯abcde❯abcdefg13.txt': '0.children.0.children.0.children.0.children.1.children.15',
* // 'root❯aaa❯aaabbb❯abc❯abcde❯abcdefg14.txt': '0.children.0.children.0.children.0.children.1.children.16',
* // 'root❯aaa❯aaabbb❯abc❯abcde❯abcdefg15.txt': '0.children.0.children.0.children.0.children.1.children.17',
* // 'root❯aaa❯aaabbb❯abc❯abcde❯abcdefg16.txt': '0.children.0.children.0.children.0.children.1.children.18',
* // 'root❯aaa❯aaabbb❯abc❯abcde❯abcdefg17.txt': '0.children.0.children.0.children.0.children.1.children.19',
* // 'root❯aaa❯aaabbb❯abc❯abcde❯abcdefg18.txt': '0.children.0.children.0.children.0.children.1.children.20',
* // 'root❯aaa❯aaabbb❯abc❯abcde❯abcdefg19.txt': '0.children.0.children.0.children.0.children.1.children.21',
* // 'root❯aaa❯aaabbb❯abc❯abcde❯abcdefg20.txt': '0.children.0.children.0.children.0.children.1.children.22',
* // 'root❯aaa❯aaabbb❯abc❯abcd❯abcde.txt': '0.children.0.children.0.children.0.children.0.children.0',
* // 'root❯中文路徑2❯aaa❯aaabbb❯abc❯測試.txt': '0.children.8.children.0.children.0.children.0.children.0'
* // },
* // fpsNormalize: [
* // { type: 'folder', path: '/root/aaa' },
* // { type: 'file', path: '/root/aaa1.txt' },
* // { type: 'file', path: '/root/aaa2.txt' },
* // { type: 'folder', path: '/root/aaa/aaabbb' },
* // { type: 'file', path: '/root/aaa/aaabbb.txt' },
* // { type: 'file', path: '/root/aaa/aaabbb/aaabbbccc.txt' },
* // { type: 'folder', path: '/root/aaa/aaabbb/abc' },
* // { type: 'folder', path: '/root/aaa/aaabbb/abc/abcd' },
* // { type: 'file', path: '/root/aaa/aaabbb/abc/abcd/abcde.txt' },
* // { type: 'folder', path: '/root/aaa/aaabbb/abc/abcde' },
* // { type: 'file', path: '/root/aaa/aaabbb/abc/abcde/abcdef1.txt' },
* // { type: 'file', path: '/root/aaa/aaabbb/abc/abcde/abcdef2.txt' },
* // {
* // type: 'file',
* // path: '/root/aaa/aaabbb/abc/abcde/abcdef3 aaa bbb ccc dddddd eeeeeee fffffffffff ggggggggggggg.txt'
* // },
* // { type: 'file', path: '/root/aaa/aaabbb/abc/abcde/abcdefg01.txt' },
* // { type: 'file', path: '/root/aaa/aaabbb/abc/abcde/abcdefg02.txt' },
* // { type: 'file', path: '/root/aaa/aaabbb/abc/abcde/abcdefg03.txt' },
* // { type: 'file', path: '/root/aaa/aaabbb/abc/abcde/abcdefg04.txt' },
* // { type: 'file', path: '/root/aaa/aaabbb/abc/abcde/abcdefg05.txt' },
* // { type: 'file', path: '/root/aaa/aaabbb/abc/abcde/abcdefg06.txt' },
* // { type: 'file', path: '/root/aaa/aaabbb/abc/abcde/abcdefg07.txt' },
* // { type: 'file', path: '/root/aaa/aaabbb/abc/abcde/abcdefg08.txt' },
* // { type: 'file', path: '/root/aaa/aaabbb/abc/abcde/abcdefg09.txt' },
* // { type: 'file', path: '/root/aaa/aaabbb/abc/abcde/abcdefg10.txt' },
* // { type: 'file', path: '/root/aaa/aaabbb/abc/abcde/abcdefg11.txt' },
* // { type: 'file', path: '/root/aaa/aaabbb/abc/abcde/abcdefg12.txt' },
* // { type: 'file', path: '/root/aaa/aaabbb/abc/abcde/abcdefg13.txt' },
* // { type: 'file', path: '/root/aaa/aaabbb/abc/abcde/abcdefg14.txt' },
* // { type: 'file', path: '/root/aaa/aaabbb/abc/abcde/abcdefg15.txt' },
* // { type: 'file', path: '/root/aaa/aaabbb/abc/abcde/abcdefg16.txt' },
* // { type: 'file', path: '/root/aaa/aaabbb/abc/abcde/abcdefg17.txt' },
* // { type: 'file', path: '/root/aaa/aaabbb/abc/abcde/abcdefg18.txt' },
* // { type: 'file', path: '/root/aaa/aaabbb/abc/abcde/abcdefg19.txt' },
* // { type: 'file', path: '/root/aaa/aaabbb/abc/abcde/abcdefg20.txt' },
* // { type: 'file', path: '/root/aaa/aaabbb/abc/abcd1.txt' },
* // { type: 'file', path: '/root/aaa/aaabbb/abc/abcd2.txt' },
* // { type: 'folder', path: '/root/bbb' },
* // { type: 'folder', path: '/root/ccc' },
* // { type: 'folder', path: '/root/ccc/cccddd' },
* // { type: 'file', path: '/root/ccc/cccddd/cccdddeee.txt' },
* // { type: 'folder', path: '/root/eee' },
* // { type: 'folder', path: '/root/eee/eeefff1' },
* // { type: 'folder', path: '/root/eee/eeefff2' },
* // { type: 'folder', path: '/root/ggg' },
* // { type: 'folder', path: '/root/c:' },
* // { type: 'folder', path: '/root/c:/hhh' },
* // { type: 'folder', path: '/root/aaaa' },
* // {
* // type: 'folder',
* // path: '/root/aaaa/bbbbbb cccccccccccc ddd dd ddd ddd ddd eeeeeeeeeeee ffff'
* // },
* // {
* // type: 'file',
* // path: '/root/aaaa/bbbbbb cccccccccccc ddd dd ddd ddd ddd eeeeeeeeeeee ffff/gfedcba.txt'
* // },
* // {
* // type: 'folder',
* // path: '/root/aaaa/bbbbbb cccccccccccc ddd dd ddd ddd ddd eeeeeeeeeeee ffff/ggg'
* // },
* // {
* // type: 'file',
* // path: '/root/aaaa/bbbbbb cccccccccccc ddd dd ddd ddd ddd eeeeeeeeeeee ffff/ggg/hhh.txt'
* // },
* // { type: 'folder', path: '/root/d:' },
* // { type: 'folder', path: '/root/d:/中文路徑1' },
* // { type: 'folder', path: '/root/中文路徑2' },
* // { type: 'folder', path: '/root/中文路徑2/aaa' },
* // { type: 'folder', path: '/root/中文路徑2/aaa/aaabbb' },
* // { type: 'folder', path: '/root/中文路徑2/aaa/aaabbb/abc' },
* // { type: 'file', path: '/root/中文路徑2/aaa/aaabbb/abc/測試.txt' }
* // ]
* // }
*
* let fps5 = [{ 'type': 'folder', 'path': '/aaa1' }, { 'type': 'folder', 'path': '/aaa2' }, { 'type': 'folder', 'path': '/aaa10' }, { 'type': 'file', 'path': '/aaa/bbb/z1.txt' }, { 'type': 'file', 'path': '/aaa/bbb/z2.txt' }, { 'type': 'file', 'path': '/aaa/bbb/z10.txt' }]
* let soryItems5 = (rs, pid, ns) => {
* // console.log('soryItems', 'pid=', pid, 'ns=', ns, 'rs=', rs)
* rs = arrSort(rs, { compareKey: 'text' })
* return rs
* }
* let r5 = filepathToTree(fps5, { soryItems: soryItems5 })
* //fs.writeFileSync('r5.json', JSON.stringify(r5), 'utf8')
* console.log(r5)
* console.log('\n\n')
* // => {
* // treeItems: [
* // {
* // _indOri: null,
* // _indNormalize: null,
* // ns: 1,
* // ts: [Array],
* // pathInfors: [Array],
* // _type: 'folder',
* // type: 'array',
* // numOfChilren: -1,
* // id: 'root',
* // parentId: '',
* // text: 'root',
* // children: [Array],
* // data: null
* // }
* // ],
* // treeItemsFolder: [
* // {
* // _indOri: null,
* // _indNormalize: null,
* // ns: 1,
* // ts: [Array],
* // pathInfors: [Array],
* // _type: 'folder',
* // type: 'array',
* // numOfChilren: -1,
* // id: 'root',
* // parentId: '',
* // text: 'root',
* // children: [Array],
* // data: null
* // }
* // ],
* // kpPath: {
* // root: '0',
* // 'root❯aaa': '0.children.0',
* // 'root❯aaa1': '0.children.1',
* // 'root❯aaa10': '0.children.2',
* // 'root❯aaa2': '0.children.3',
* // 'root❯aaa❯bbb': '0.children.0.children.0',
* // 'root❯aaa❯bbb❯z1.txt': '0.children.0.children.0.children.0',
* // 'root❯aaa❯bbb❯z2.txt': '0.children.0.children.0.children.1',
* // 'root❯aaa❯bbb❯z10.txt': '0.children.0.children.0.children.2'
* // },
* // fpsNormalize: [
* // { type: 'folder', path: '/root/aaa1' },
* // { type: 'folder', path: '/root/aaa2' },
* // { type: 'folder', path: '/root/aaa10' },
* // { type: 'folder', path: '/root/aaa' },
* // { type: 'folder', path: '/root/aaa/bbb' },
* // { type: 'file', path: '/root/aaa/bbb/z1.txt' },
* // { type: 'file', path: '/root/aaa/bbb/z2.txt' },
* // { type: 'file', path: '/root/aaa/bbb/z10.txt' }
* // ]
* // }
*
* let fps6 = [{ 'type': 'folder', 'path': '/aaa1' }, { 'type': 'folder', 'path': '/aaa2' }, { 'type': 'folder', 'path': '/aaa10' }, { 'type': 'file', 'path': '/aaa/bbb/z1.txt' }, { 'type': 'file', 'path': '/aaa/bbb/z2.txt' }, { 'type': 'file', 'path': '/aaa/bbb/z10.txt' }]
* let soryItems6 = (rs, pid, ns) => {
* // console.log('soryItems', 'pid=', pid, 'ns=', ns, 'rs=', rs)
* rs = arrSort(rs, { compareKey: 'text', localeCompare: true })
* return rs
* }
* let r6 = filepathToTree(fps6, { soryItems: soryItems6 })
* //fs.writeFileSync('r6.json', JSON.stringify(r6), 'utf8')
* console.log(r6)
* console.log('\n\n')
* // => {
* // treeItems: [
* // {
* // _indOri: null,
* // _indNormalize: null,
* // ns: 1,
* // ts: [Array],
* // pathInfors: [Array],
* // _type: 'folder',
* // type: 'array',
* // numOfChilren: -1,
* // id: 'root',
* // parentId: '',
* // text: 'root',
* // children: [Array],
* // data: null
* // }
* // ],
* // treeItemsFolder: [
* // {
* // _indOri: null,
* // _indNormalize: null,
* // ns: 1,
* // ts: [Array],
* // pathInfors: [Array],
* // _type: 'folder',
* // type: 'array',
* // numOfChilren: -1,
* // id: 'root',
* // parentId: '',
* // text: 'root',
* // children: [Array],
* // data: null
* // }
* // ],
* // kpPath: {
* // root: '0',
* // 'root❯aaa': '0.children.0',
* // 'root❯aaa1': '0.children.1',
* // 'root❯aaa2': '0.children.2',
* // 'root❯aaa10': '0.children.3',
* // 'root❯aaa❯bbb': '0.children.0.children.0',
* // 'root❯aaa❯bbb❯z1.txt': '0.children.0.children.0.children.0',
* // 'root❯aaa❯bbb❯z2.txt': '0.children.0.children.0.children.1',
* // 'root❯aaa❯bbb❯z10.txt': '0.children.0.children.0.children.2'
* // },
* // fpsNormalize: [
* // { type: 'folder', path: '/root/aaa1' },
* // { type: 'folder', path: '/root/aaa2' },
* // { type: 'folder', path: '/root/aaa10' },
* // { type: 'folder', path: '/root/aaa' },
* // { type: 'folder', path: '/root/aaa/bbb' },
* // { type: 'file', path: '/root/aaa/bbb/z1.txt' },
* // { type: 'file', path: '/root/aaa/bbb/z2.txt' },
* // { type: 'file', path: '/root/aaa/bbb/z10.txt' }
* // ]
* // }
*
* //先建虛擬資料夾再補建資料夾
* let fps7 = [
* { 'type': 'folder', 'path': '/aaa', 'ext': 'ext1' },
* { 'type': 'file', 'path': '/aaa/bbb/z1.txt', 'ext': 'ext2' },
* { 'type': 'folder', 'path': '/aaa/bbb', 'ext': 'ext3' },
* ]
* let r7 = filepathToTree(fps7)
* // fs.writeFileSync('r7.json', JSON.stringify(r7), 'utf8')
* console.log(r7)
* console.log('\n\n')
* // => {
* // treeItems: [
* // {
* // _indOri: null,
* // _indNormalize: null,
* // ns: 1,
* // ts: [Array],
* // pathInfors: [Array],
* // _type: 'folder',
* // type: 'array',
* // numOfChilren: -1,
* // id: 'root',
* // parentId: '',
* // text: 'root',
* // children: [Array],
* // data: null
* // }
* // ],
* // treeItemsFolder: [
* // {
* // _indOri: null,
* // _indNormalize: null,
* // ns: 1,
* // ts: [Array],
* // pathInfors: [Array],
* // _type: 'folder',
* // type: 'array',
* // numOfChilren: -1,
* // id: 'root',
* // parentId: '',
* // text: 'root',
* // children: [Array],
* // data: null
* // }
* // ],
* // kpPath: {
* // root: '0',
* // 'root❯aaa': '0.children.0',
* // 'root❯aaa❯bbb': '0.children.0.children.0',
* // 'root❯aaa❯bbb❯z1.txt': '0.children.0.children.0.children.0'
* // },
* // fpsNormalize: [
* // { type: 'folder', path: '/root/aaa', ext: 'ext1' },
* // { type: 'folder', path: '/root/aaa/bbb', ext: 'ext3' },
* // { type: 'file', path: '/root/aaa/bbb/z1.txt', ext: 'ext2' }
* // ]
* // }
*
* //先建虛擬資料夾再補建檔案須報錯
* let fps8 = [
* { 'type': 'folder', 'path': '/aaa', 'ext': 'ext1' },
* { 'type': 'file', 'path': '/aaa/bbb/z1.txt', 'ext': 'ext2' },
* { 'type': 'file', 'path': '/aaa/bbb', 'ext': 'ext3' },
* ]
* let r8
* try {
* r8 = filepathToTree(fps8)
* }
* catch (err) {
* r8 = err.toString()
* }
* //fs.writeFileSync('r8.json', JSON.stringify(r8), 'utf8')
* console.log(r8)
* console.log('\n\n')
* // => Error: id[root❯aaa❯bbb] is unrecognized from a folder to file
*
* //先建檔案再補建虛擬資料夾須報錯
* let fps9 = [
* { 'type': 'folder', 'path': '/aaa', 'ext': 'ext1' },
* { 'type': 'file', 'path': '/aaa/bbb', 'ext': 'ext3' },
* { 'type': 'file', 'path': '/aaa/bbb/z1.txt', 'ext': 'ext2' },
* ]
* let r9
* try {
* r9 = filepathToTree(fps9)
* }
* catch (err) {
* r9 = err.toString()
* }
* //fs.writeFileSync('r9.json', JSON.stringify(r9), 'utf8')
* console.log(r9)
* console.log('\n\n')
* // => Error: id[root❯aaa❯bbb] is unrecognized from a file to folder
*
* //先建檔案再補建資料夾須報錯
* let fps10 = [
* { 'type': 'folder', 'path': '/aaa', 'ext': 'ext1' },
* { 'type': 'file', 'path': '/aaa/bbb', 'ext': 'ext3' },
* { 'type': 'folder', 'path': '/aaa/bbb', 'ext': 'ext4' },
* { 'type': 'file', 'path': '/aaa/bbb/z1.txt', 'ext': 'ext2' },
* ]
* let r10
* try {
* r10 = filepathToTree(fps10)
* }
* catch (err) {
* r10 = err.toString()
* }
* //fs.writeFileSync('r10.json', JSON.stringify(r10), 'utf8')
* console.log(r10)
* console.log('\n\n')
* // => Error: id[root❯aaa❯bbb] is unrecognized from a file to folder
*
*/
function filepathToTree(fps, opt = {}) {
//check
if (!isearr(fps)) {
return {}
}
//delimiter
let delimiter = get(opt, 'delimiter')
if (!isestr(delimiter)) {
delimiter = '❯' //使用特殊符號區分階層
}
//bindRoot
let bindRoot = get(opt, 'bindRoot')
if (!isestr(bindRoot)) {
bindRoot = 'root'
}
//soryItems
let soryItems = get(opt, 'soryItems')
//parseFp
let parseFp = (fp) => {
//replace
// fp = replace(fp, '\\\\', '\\')
fp = fp.replace(/\\\\/g, '\\')
// fp = replace(fp, '\\', '/')
fp = fp.replace(/\\/g, '/')
//sep
let ss = sep(fp, '/')
// //add root
// ss = [bindRoot, ...ss]
//cfp, 組裝成字串路徑
let cfp = join(ss, delimiter)
// //name
// let name = last(ss)
return {
ss,
cfp,
// name,
}
}
//parseFp
let vfps = map(fps, (v, k) => {
//check
if (v.type !== 'folder' && v.type !== 'file') {
v.type = 'file'
}
//parseFp
let r = parseFp(v.path)
// console.log('parseFp', v.path, r)
// //ns
// let ns = size(r.ss)
// //pas
// let pas = []
// if (ns - 1 > 0) {
// pas = take(r.s, ns - 1)
// }
// //pacfp
// let pacfp = ''
// if (size(pas) > 0) {
// pacfp = join(pas, delimiter)
// }
//t
let t = {
ind: k,
type: v.type,
// name: r.name,
// ns,
ss: r.ss,
// id: r.cfp,
// pas,
// parentId: pacfp,
data: cloneDeep(v),
}
return t
})
// console.log('vfps', vfps)
//genParentIds
let genParentIds = (ts) => {
//n
let n = size(ts)
//pathInfors, 各父層(含自己)資訊
let pathInfors = []
for (let i = 0; i < n; i++) {
//tss
let tss = []
for (let j = 0; j <= i; j++) {
tss.push(ts[j])
}
//id
let id = join(tss, delimiter)
// console.log(id, id)
//push
pathInfors.push({
id,
name: ts[i],
})
}
// //剔除root
// pathInfors = drop(pathInfors)
return pathInfors
}
//fpsNormalize, kpLv
let ind = -1
let fpsNormalize = []
let kpLv = {}
each(vfps, (v) => {
//偵測各層
let ts = [bindRoot] //由bindRoot當最上層
let id0 = bindRoot //由bindRoot當最上層
let nsm1 = size(v.ss) - 1
each(v.ss, (s, ks) => {
//isFolder, 當ks小於ns-1時一定為資料夾, 因有些是直接提供深層檔案沒有父層資料夾, 故得另外計算
let isFolder = ks < nsm1 || v.type === 'folder'
//ts
ts.push(s)
//indOri, data
let indOri = null
let data = null
if (ks === nsm1) {
indOri = v.ind
data = v.data
}
//id
let id = join(ts, delimiter)
// console.log(id, ks, 'isFolder', isFolder)
//pathInfors
let pathInfors = genParentIds(ts)
//check
if (!haskey(kpLv, id)) {
ind++
//indNormalize
let indNormalize = ind
//r
let r = null
if (isFolder) {
r = {
_indOri: indOri,
_indNormalize: indNormalize,
ns: size(ts),
ts: cloneDeep(ts),
pathInfors,
_type: 'folder',
type: 'array',
numOfChilren: -1, //無法計算
id,
parentId: id0,
text: s,
children: [], //資料夾預先建置children
data,
}
}
else {
r = {
_indOri: indOri,
_indNormalize: indNormalize,
ns: size(ts),
ts: cloneDeep(ts),
pathInfors,
_type: 'file',
type: 'node',
numOfChilren: -1, //無法計算
id,
parentId: id0,
text: s,
// children,
data,
}
}
kpLv[id] = r
//push, 新增項目所儲存指標為ind
fpsNormalize.push({
...get(kpLv[id], 'data', {}), //預先填入原始數據, 之後再複寫type與path, 因可能無data故得要預設為{}
type: kpLv[id]._type,
path: '/' + join(ts, '/'),
})
}
else {
//若因為給予數據未排序導致先建立虛擬資料夾時, 得偵測與更新數據
//r, 因id可能有「.」得使用[]預先取出物件, 再進行後續處理
let r = kpLv[id]
//_typeOld
let _typeOld = get(r, '_type', '')
//isFolderOld
let isFolderOld = _typeOld === 'folder'
//update
if (!isFolderOld && isFolder) {
//過去已建檔案, 但現在須新建資料夾, 非預期須報錯
console.log('kpLv[id]', r)
console.log('v', v)
throw new Error(`id[${id}] is unrecognized from a file to folder`)
}
else if (!isFolderOld && !isFolder) {
//過去已建檔案, 但現在又須新建為檔案, 數據有重複, 非預期須報錯
console.log('kpLv[id]', r)
console.log('v', v)
throw new Error(`id[${id}] get repeat data from two files`)
}
else if (isFolderOld && !isFolder) {
//過去已建資料夾或虛擬資料夾, 但現在又須新建為檔案, 數據不一致, 非預期須報錯
console.log('kpLv[id]', r)
console.log('v', v)
throw new Error(`id[${id}] is unrecognized from a folder to file`)
}
else { //isFolderOld && isFolder
//過去已建資料夾但現在又須新建為資料夾, 預期上是兩次被視為虛擬資料夾
if (iseobj(kpLv[id].data) && iseobj(data)) {
//因視為資料夾data已為實際data, 但又出現新data, 非預期須報錯
console.log('kpLv[id]', r)
console.log('v', v)
throw new Error(`id[${id}] get repeat data from two folders`)
}
else if (kpLv[id].data === null && iseobj(data)) {
//之前資料夾無data, 但出現資料夾且有data, 預期是原始數據先建置虛擬資料夾, 且後面又提供指定為資料夾數據
//update kpLv[id]
kpLv[id]._indOri = indOri
kpLv[id].data = data
//update fpsNormalize
let ind = kpLv[id]._indNormalize
let t = fpsNormalize[ind]
fpsNormalize[ind] = {
...data,
type: t.type,
path: t.path,
}
}
else { //kpLv[id].data !== null && data === null
//無data代表不須更新data
}
}
}
//update id0
id0 = id
})
})
// console.log('kpLv', kpLv)
// console.log('fpsNormalize', fpsNormalize)
//vLv
let vLv = values(kpLv)
// console.log('vLv', vLv)
//sortBy
vLv = sortBy(vLv, 'id') //用id排序
vLv = sortBy(vLv, 'ns') //用階層數排序, 少的放前
// console.log('vLv', vLv)
//soryItems
if (isfun(soryItems)) {
let vLvTemp = []
let g1Lv = groupBy(vLv, 'ns')
each(g1Lv, (g1v, g1k) => {
let g2Lv = groupBy(g1v, 'parentId')
each(g2Lv, (g2v, g2k) => {
//soryItems
// console.log('soryItems before', map(g2v, 'text'))
let r = soryItems(g2v, g2k, g1k)
// console.log('soryItems after', map(r, 'text'))
//merge
vLvTemp = [
...vLvTemp,
...r,
]
})
})
vLv = vLvTemp
}
//vLvFd, vLvFl
let vLvFd = []
let vLvFl = []
each(vLv, (v) => {
if (v._type === 'folder') {
vLvFd.push(v)
}
else {
vLvFl.push(v)
}
})
// console.log('vLvFd', vLvFd)
// console.log('vLvFl', vLvFl)
//gdataFd, gdataFl
let gdataFd = groupBy(vLvFd, 'ns')
let gdataFl = groupBy(vLvFl, 'ns')
// console.log('gdataFd', gdataFd)
// console.log('gdataFl', gdataFl)
//nsMaxFd, nsMaxFl 最大層數
let nsMaxFd = max(map(vLvFd, 'ns'))
let nsMaxFl = max(map(vLvFl, 'ns'))
//treeItems
let treeItems = null
if (true) {
let ts = [bindRoot]
treeItems = [
{
_indOri: null,
_indNormalize: null,
ns: size(ts),
ts: cloneDeep(ts),
pathInfors: genParentIds(ts),
_type: 'folder',
type: 'array',
numOfChilren: -1, //無法計算
id: bindRoot, //給予bindRoot為主鍵
parentId: '', //無上層id
text: bindRoot,
children: [], //未避免後續為最末端資料夾不能靠偵測建置, 一律改成預設建置
data: null,
}
]
}
//kpPathFd, kpPath, 從上層開始建構folder, file之後再建
let kpPathFd = {}
kpPathFd[bindRoot] = `0.children` //儲存自己子資料夾路徑, 預建bindRoot故為第1個也只有1個
let kpPath = {}
kpPath[bindRoot] = `0` //儲存自己(資料夾)路徑
for (let i = 2; i <= nsMaxFd; i++) { //有root在故從2開始
//data
let data = gdataFd[i]
// console.log(i, 'folder data', data)
each(data, (v) => {
// console.log('each folder', v, v.id)
//k, 父層取鍵
let k = get(kpPathFd, v.parentId, '')
// console.log('父層取鍵', k)
//check
if (!isestr(k)) {
console.log('kpPathFd', kpPathFd)
console.log('v', v, v.parentId)
throw new Error(`invalid parentId[${v.parentId}] in folder`)
}
//chds
let chds = get(treeItems, k)
// console.log('chds', chds, map(chds, 'id'))
//set
if (!isarr(chds)) { //因先處理資料夾, 故此處一定需為資料夾
set(treeItems, k, [])
}
//重取chds, 因若有重建則須重取
chds = get(treeItems, k)
//chd
let chd = find(chds, { id: v.id })
//check
if (!iseobj(chd)) {
//push, 加入本層資料夾
chds.push({
...v,
children: [], //未避免後續為最末端資料夾不能靠偵測建置, 一律改成預設建置
})
//save
let c = `${k}.${size(chds) - 1}`
kpPathFd[v.id] = `${c}.children` //儲存自己子資料夾路徑
kpPath[v.id] = c //儲存自己(資料夾)路徑
}
})
}
// console.log('treeItems(add folder)', cloneDeep(treeItems))
// console.log('kpPathFd', kpPathFd)
//cloneDeep
let treeItemsFolder = cloneDeep(treeItems)
// console.log('treeItemsFolder', treeItemsFolder)
//kpPathFl, 從上層開始建構file
let kpPathFl = {}
for (let i = 2; i <= nsMaxFl; i++) { //有root在故從2開始
//data
let data = gdataFl[i]
// console.log(i, 'file data', data)
each(data, (v) => {
// console.log('each file', v, v.id)
//k, 父層取鍵
let k = get(kpPathFd, v.parentId, '')
// console.log('父層取鍵', k)
//check
if (!isestr(k)) {
console.log('kpPathFd', kpPathFd)
console.log('v', v, v.parentId)
throw new Error(`invalid parentId[${v.parentId}] in file`)
}
//chds
let chds = get(treeItems, k)
// console.log('chds', chds, map(chds, 'id'))
//set
if (!isarr(chds)) { //因先處理資料夾, 故此處一定需為資料夾
console.log('treeItems', treeItems)
console.log('k', k)
console.log('v', v)
throw new Error('無預先建置資料夾')
}
//chd
let chd = find(chds, { id: v.id })
//check
if (!iseobj(chd)) {
//push, 加入本層檔案
chds.push(v)
//save
let c = `${k}.${size(chds) - 1}`
kpPathFl[v.id] = c //儲存自己檔案路徑
kpPath[v.id] = c //儲存自己(檔案)路徑
}
})
}
// console.log('treeItems(add file)', cloneDeep(treeItems))
// console.log('kpPathFl', kpPathFl)
// //remove root for kpPath
// each(kpPath, (v, k) => {
// // console.log(k, 'v1', v)
// v = strdelleft(v, 11)
// // console.log(k, 'v2', v)
// kpPath[k] = v
// })
// // console.log('kpPath', kpPath)
// //remove root for treeItems, treeItemsFolder
// treeItems = get(treeItems, '0.children')
// treeItemsFolder = get(treeItemsFolder, '0.children')
return {
treeItems,
treeItemsFolder,
kpPath,
fpsNormalize,
}
}
export default filepathToTree