如何实现数组层级文件目录结构的指定路径存在性检查?
目录路径匹配功能实现
需要实现的功能:针对数组层级存储的文件目录结构,通过System->Main->Drivers这类路径字符串,检查对应资源(目录或文件)是否存在,并返回匹配结果:
- 输入存在的目录路径,返回该目录下的文件列表;
- 输入存在的文件路径,返回该文件对象;
- 输入不存在的路径,返回undefined或NaN。
我耗时3小时编写的代码完全无法满足需求,最终在Michael M.和chill 389cc的帮助下完成了正确的has方法实现,相关代码如下:
示例代码
const Files = [ { Name: 'System', Type: 'directory', Value: [ { Name: 'Main', Type: 'directory', Value: [ { Name: 'Drivers', Type: 'directory', Value: [ { Name: 'Startup', Type: 'file', Value: new FileSystem.File('Startup', 0x1, 'test blah blah'), }, ], }, ], }, ], }, ]; BlahBlah.has(Files, 'System->Main->Drivers'); // 返回该目录下的文件列表 [File] BlahBlah.has(Files, 'System->Main->Drivers->Startup'); // 返回对应文件对象 File BlahBlah.has(Files, 'System->Main->Drivers->AnyWhere'); // 返回 undefined BlahBlah.has(Files, 'System->Main->AnyRandomDirectory'); // 返回 NaN
初始实现代码
function text2Binary(str: string, spliter: string = ' '): string { return str .split('') .map(function (char) { return char.charCodeAt(0).toString(2); }) .join(spliter); } export function FileTypeFromNumber(e: number) { if (typeof e != 'number') try { e = Number(e); } catch (_) { return null; } return { 0x1: { Name: 'Executable File', Extension: 'exe', }, 0x2: { Name: 'Text Document', Extension: 'txt', }, }[e]; } export type FileTypes = | 0x1 | 0x2 | 0x3 | 0x4 | 0x5 | 0x6 | 0x7 | 0x8 | 0x9 | null; export class File { Name: string; Type: { Name: string; Extension: string; }; Content: string; Size: number; constructor(name: string, type: FileTypes, content: string) { this.Name = name; this.Type = FileTypeFromNumber(type); this.Content = content; this.Size = text2Binary(content, '').length; } } export class Directory { public Name: string; public Files: (File | Directory)[] = []; constructor(name: string) { this.Name = name; } addFile(file: File | Directory) { this.Files.push(file); } getFile(name: string): null | (File | Directory)[] { if (typeof name != 'string') try { name = String(name); } catch (_) { return null; } const Result = this.Files.filter((e) => e.Name == name); return Result.length == 0 ? null : Result; } getSize() { return this.Files.map((e) => e instanceof Directory ? e.getSize() : e.Size ).reduce((a, b) => a + b, 0); } has(name) { return this.Files.some((e) => e.Name == name); } getJSON() { return this.Files.map((e) => ({ ...e })); } } interface x { Content: string; Name: string; Size: number; Type: string; } export function ConvertFromJSONtoDirectory(json: any[]) { return json.map((value) => { const isDirectory = value.Type == 'directory'; if (!isDirectory) { return value.Value; } const self = new Directory(value.Name); ConvertFromJSONtoDirectory(value.Value).map((e) => self.addFile(e)); return self; }); } export default class DirectorySystem { Memory: Map<any, any>; Current: string | null; constructor(Current = null) { this.Memory = new Map(); this.Current = Current; } addDirectory(directory: Directory): null | true { if (!(directory instanceof Directory)) return null; if (this.Memory.has(directory.Name)) return null; this.Memory.set(directory.Name, directory); return true; } getDirectory(DirectoryName: string): boolean | Directory { if (typeof DirectoryName != 'string') try { DirectoryName = String(DirectoryName); } catch (_) { return null; } const Result = this.Memory.has(DirectoryName); return Result ? this.Memory.get(DirectoryName) : Result; } getDirectoryCurrent() { if (this.Current == null) return this; } changeDirectory(by: -1 | 1, value: string) { if (by == -1) { if (this.Current == null) return null; if (this.Current.includes('->')) { this.Current = this.Current.split('->').slice(0, -1).join('->'); } else { this.Current = null; } return this.Current; } else if (by == 1) { let Position = [this.Current, value].join('->'); if (this.Current == null) { Position = Position.split('->').slice(1).join('->'); } let Result = this.has(Position); console.log(Result); } } has(query: string) { try { return query.split('->').reduce((a, b) => { if (Array.isArray(a)) { const f = a.filter((e) => e['Name'] == b); if (a.length > 0) { return f['Files']; } else { return a; } } return a['Files']; }, this.getJSON()); } catch (_) { return false; } } getJSON(): x[][] { return [...this.Memory.values()].reduce((a, b) => { a[b.Name] = b.getJSON(); return a; }, {}); } }
最终正确实现的has方法
has( query: string, overwrite = null ) { // 如果overwrite参数不为空,则使用该参数作为初始资源列表 let files = overwrite == null ? this.getJSON() : overwrite; // 将路径字符串分割为节点数组,过滤空字符串 const QueryParams = query.split('->').filter(String); // 无查询参数时返回当前传入的资源列表 if (QueryParams.length == 0) return overwrite; if (Array.isArray(files)) { const SearchFor = QueryParams.shift(); // 在当前层级查找匹配名称的资源 const Result = files.filter((e) => { if (e instanceof Directory) { return e.Name == SearchFor; } return e.Name == SearchFor; })[0]; // 未找到匹配资源,返回undefined if (!Result) return undefined; // 找到文件且无剩余路径节点,返回该文件对象 if (Result instanceof File) return QueryParams.length == 0 ? Result : undefined; // 找到目录且无剩余路径节点,返回该目录下的文件列表 if (Result instanceof Directory && QueryParams.length == 0) return Result.Files; // 名称不匹配或仍有路径节点但目录为空,返回NaN if ( Result.Name != SearchFor || (QueryParams.length != 0 && Result.Files.length == 0) ) return NaN; // 递归调用,继续处理剩余路径节点 return this.has(QueryParams.join('->'), Result.Files); } else { // 处理对象类型的资源结构,查找对应节点 const Result = files[QueryParams.shift()]; return !Result ? undefined : this.has(QueryParams.join('->'), Result); } }
内容的提问来源于stack exchange,提问作者Wraithdev2
相关产品推荐
相关产品推荐

