chore: update helpers

This commit is contained in:
vben
2024-06-02 10:29:17 +08:00
parent 8f1b054bb1
commit fc423c3657
19 changed files with 361 additions and 353 deletions

View File

@@ -7,7 +7,7 @@
"repository": {
"type": "git",
"url": "git+https://github.com/vbenjs/vue-vben-admin.git",
"directory": "packages/@vben-core/helpers"
"directory": "packages/@vben-core/forward/helpers"
},
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
"scripts": {

View File

@@ -0,0 +1,132 @@
import { describe, expect, it } from 'vitest';
import { flattenObject } from './flatten-object';
describe('flattenObject', () => {
it('should flatten a nested object correctly', () => {
const nestedObject = {
language: 'en',
notifications: {
email: true,
push: {
sound: true,
vibration: false,
},
},
theme: 'light',
};
const expected = {
language: 'en',
notificationsEmail: true,
notificationsPushSound: true,
notificationsPushVibration: false,
theme: 'light',
};
const result = flattenObject(nestedObject);
expect(result).toEqual(expected);
});
it('should handle empty objects', () => {
const nestedObject = {};
const expected = {};
const result = flattenObject(nestedObject);
expect(result).toEqual(expected);
});
it('should handle objects with primitive values', () => {
const nestedObject = {
active: true,
age: 30,
name: 'Alice',
};
const expected = {
active: true,
age: 30,
name: 'Alice',
};
const result = flattenObject(nestedObject);
expect(result).toEqual(expected);
});
it('should handle objects with null values', () => {
const nestedObject = {
user: {
age: null,
name: null,
},
};
const expected = {
userAge: null,
userName: null,
};
const result = flattenObject(nestedObject);
expect(result).toEqual(expected);
});
it('should handle nested empty objects', () => {
const nestedObject = {
a: {},
b: { c: {} },
};
const expected = {};
const result = flattenObject(nestedObject);
expect(result).toEqual(expected);
});
it('should handle arrays within objects', () => {
const nestedObject = {
hobbies: ['reading', 'gaming'],
name: 'Alice',
};
const expected = {
hobbies: ['reading', 'gaming'],
name: 'Alice',
};
const result = flattenObject(nestedObject);
expect(result).toEqual(expected);
});
it('should flatten objects with nested arrays correctly', () => {
const nestedObject = {
person: {
hobbies: ['reading', 'gaming'],
name: 'Alice',
},
};
const expected = {
personHobbies: ['reading', 'gaming'],
personName: 'Alice',
};
const result = flattenObject(nestedObject);
expect(result).toEqual(expected);
});
it('should handle objects with undefined values', () => {
const nestedObject = {
user: {
age: undefined,
name: 'Alice',
},
};
const expected = {
userAge: undefined,
userName: 'Alice',
};
const result = flattenObject(nestedObject);
expect(result).toEqual(expected);
});
});

View File

@@ -1,21 +1,6 @@
import type { Flatten } from '@vben-core/typings';
import {
capitalizeFirstLetter,
toLowerCaseFirstLetter,
} from '@vben-core/toolkit';
/**
*
* @param key
* @param parentKey
*/
function toCamelCase(key: string, parentKey: string): string {
if (!parentKey) {
return key;
}
return parentKey + key.charAt(0).toUpperCase() + key.slice(1);
}
import { capitalizeFirstLetter } from '@vben-core/toolkit';
/**
*
@@ -70,74 +55,7 @@ function flattenObject<T extends Record<string, any>>(
return result as Flatten<T>;
}
/**
*
*
* @template T -
* @param {Record<string, T>} obj -
* @param {number} level -
* @returns {T}
*
* @example
* 1
* const flatObject = {
* 'commonAppName': 1,
* 'anotherKeyExample': 2,
* 'someOtherKey': 3
* };
* const nestedObject = toNestedObject(flatObject, 1);
* console.log(nestedObject);
* :
* {
* commonAppName: 1,
* anotherKeyExample: 2,
* someOtherKey: 3
* }
*
* @example
* 2
* const flatObject = {
* 'appCommonName': 1,
* 'appAnotherKeyExample': 2,
* 'appSomeOtherKey': 3
* };
* const nestedObject = toNestedObject(flatObject, 2);
* console.log(nestedObject);
* :
* {
* app: {
* commonName: 1,
* anotherKeyExample: 2,
* someOtherKey: 3
* }
* }
*/
function toNestedObject<T>(obj: Record<string, T>, level: number): T {
const result: any = {};
for (const key in obj) {
const keys = key.split(/(?=[A-Z])/);
// 将驼峰式分割为数组;
let current = result;
for (let i = 0; i < keys.length; i++) {
const lowerKey = keys[i].toLowerCase();
if (i === level - 1) {
const remainingKeys = keys.slice(i).join(''); // 保留后续部分作为键的一部分
current[toLowerCaseFirstLetter(remainingKeys)] = obj[key];
break;
} else {
current[lowerKey] = current[lowerKey] || {};
current = current[lowerKey];
}
}
}
return result as T;
}
export { flattenObject, toCamelCase, toNestedObject };
export { flattenObject };
// 定义递归类型,用于推断扁平化后的对象类型
// 限制递归深度的辅助类型

View File

@@ -0,0 +1,2 @@
export * from './flatten-object';
export * from './nested-object';

View File

@@ -0,0 +1,97 @@
import { describe, expect, it } from 'vitest';
import { nestedObject } from './nested-object';
describe('nestedObject', () => {
it('should convert flat object to nested object with level 1', () => {
const flatObject = {
anotherKeyExample: 2,
commonAppName: 1,
someOtherKey: 3,
};
const expectedNestedObject = {
anotherKeyExample: 2,
commonAppName: 1,
someOtherKey: 3,
};
expect(nestedObject(flatObject, 1)).toEqual(expectedNestedObject);
});
it('should convert flat object to nested object with level 2', () => {
const flatObject = {
appAnotherKeyExample: 2,
appCommonName: 1,
appSomeOtherKey: 3,
};
const expectedNestedObject = {
app: {
anotherKeyExample: 2,
commonName: 1,
someOtherKey: 3,
},
};
expect(nestedObject(flatObject, 2)).toEqual(expectedNestedObject);
});
it('should convert flat object to nested object with level 3', () => {
const flatObject = {
appAnotherKeyExampleValue: 2,
appCommonNameKey: 1,
appSomeOtherKeyItem: 3,
};
const expectedNestedObject = {
app: {
another: {
keyExampleValue: 2,
},
common: {
nameKey: 1,
},
some: {
otherKeyItem: 3,
},
},
};
expect(nestedObject(flatObject, 3)).toEqual(expectedNestedObject);
});
it('should handle empty object', () => {
const flatObject = {};
const expectedNestedObject = {};
expect(nestedObject(flatObject, 1)).toEqual(expectedNestedObject);
});
it('should handle single key object', () => {
const flatObject = {
singleKey: 1,
};
const expectedNestedObject = {
singleKey: 1,
};
expect(nestedObject(flatObject, 1)).toEqual(expectedNestedObject);
});
it('should handle complex keys', () => {
const flatObject = {
anotherComplexKeyWithParts: 2,
complexKeyWithMultipleParts: 1,
};
const expectedNestedObject = {
anotherComplexKeyWithParts: 2,
complexKeyWithMultipleParts: 1,
};
expect(nestedObject(flatObject, 1)).toEqual(expectedNestedObject);
});
});

View File

@@ -0,0 +1,70 @@
import { toLowerCaseFirstLetter } from '@vben-core/toolkit';
/**
* 将扁平对象转换为嵌套对象。
*
* @template T - 输入对象值的类型
* @param {Record<string, T>} obj - 要转换的扁平对象
* @param {number} level - 嵌套的层级
* @returns {T} 嵌套对象
*
* @example
* 将扁平对象转换为嵌套对象,嵌套层级为 1
* const flatObject = {
* 'commonAppName': 1,
* 'anotherKeyExample': 2,
* 'someOtherKey': 3
* };
* const nestedObject = nestedObject(flatObject, 1);
* console.log(nestedObject);
* 输出:
* {
* commonAppName: 1,
* anotherKeyExample: 2,
* someOtherKey: 3
* }
*
* @example
* 将扁平对象转换为嵌套对象,嵌套层级为 2
* const flatObject = {
* 'appCommonName': 1,
* 'appAnotherKeyExample': 2,
* 'appSomeOtherKey': 3
* };
* const nestedObject = nestedObject(flatObject, 2);
* console.log(nestedObject);
* 输出:
* {
* app: {
* commonName: 1,
* anotherKeyExample: 2,
* someOtherKey: 3
* }
* }
*/
function nestedObject<T>(obj: Record<string, T>, level: number): T {
const result: any = {};
for (const key in obj) {
const keys = key.split(/(?=[A-Z])/);
// 将驼峰式分割为数组;
let current = result;
for (let i = 0; i < keys.length; i++) {
const lowerKey = keys[i].toLowerCase();
if (i === level - 1) {
const remainingKeys = keys.slice(i).join(''); // 保留后续部分作为键的一部分
current[toLowerCaseFirstLetter(remainingKeys)] = obj[key];
break;
} else {
current[lowerKey] = current[lowerKey] || {};
current = current[lowerKey];
}
}
}
return result as T;
}
export { nestedObject };

View File

@@ -7,7 +7,7 @@
"repository": {
"type": "git",
"url": "git+https://github.com/vbenjs/vue-vben-admin.git",
"directory": "packages/@vben-core/preferences"
"directory": "packages/@vben-core/forward/preferences"
},
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
"scripts": {

View File

@@ -5,7 +5,7 @@ import type {
} from '@vben-core/typings';
import { StorageManager } from '@vben-core/cache';
import { flattenObject, toNestedObject } from '@vben-core/helpers';
import { flattenObject, nestedObject } from '@vben-core/helpers';
import { convertToHslCssVar, merge } from '@vben-core/toolkit';
import {
@@ -190,7 +190,7 @@ class PreferenceManager {
* @param {FlattenObject<Preferences>} newValue - 新的扁平对象
*/
private updateState(newValue: Flatten<Preferences>) {
const nestObj = toNestedObject(newValue, 2);
const nestObj = nestedObject(newValue, 2);
Object.assign(this.state, merge(nestObj, this.state));
}

View File

@@ -7,7 +7,7 @@
"repository": {
"type": "git",
"url": "git+https://github.com/vbenjs/vue-vben-admin.git",
"directory": "packages/@vben-core/stores"
"directory": "packages/@vben-core/forward/stores"
},
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
"scripts": {

View File

@@ -1 +0,0 @@
export * from './object';

View File

@@ -1,245 +0,0 @@
import { describe, expect, it } from 'vitest';
import { flattenObject, toCamelCase, toNestedObject } from './object';
describe('toCamelCase', () => {
it('should return the key if parentKey is empty', () => {
expect(toCamelCase('child', '')).toBe('child');
});
it('should combine parentKey and key in camel case', () => {
expect(toCamelCase('child', 'parent')).toBe('parentChild');
});
it('should handle empty key and parentKey', () => {
expect(toCamelCase('', '')).toBe('');
});
it('should handle key with capital letters', () => {
expect(toCamelCase('Child', 'parent')).toBe('parentChild');
expect(toCamelCase('Child', 'Parent')).toBe('ParentChild');
});
});
describe('flattenObject', () => {
it('should flatten a nested object correctly', () => {
const nestedObject = {
language: 'en',
notifications: {
email: true,
push: {
sound: true,
vibration: false,
},
},
theme: 'light',
};
const expected = {
language: 'en',
notificationsEmail: true,
notificationsPushSound: true,
notificationsPushVibration: false,
theme: 'light',
};
const result = flattenObject(nestedObject);
expect(result).toEqual(expected);
});
it('should handle empty objects', () => {
const nestedObject = {};
const expected = {};
const result = flattenObject(nestedObject);
expect(result).toEqual(expected);
});
it('should handle objects with primitive values', () => {
const nestedObject = {
active: true,
age: 30,
name: 'Alice',
};
const expected = {
active: true,
age: 30,
name: 'Alice',
};
const result = flattenObject(nestedObject);
expect(result).toEqual(expected);
});
it('should handle objects with null values', () => {
const nestedObject = {
user: {
age: null,
name: null,
},
};
const expected = {
userAge: null,
userName: null,
};
const result = flattenObject(nestedObject);
expect(result).toEqual(expected);
});
it('should handle nested empty objects', () => {
const nestedObject = {
a: {},
b: { c: {} },
};
const expected = {};
const result = flattenObject(nestedObject);
expect(result).toEqual(expected);
});
it('should handle arrays within objects', () => {
const nestedObject = {
hobbies: ['reading', 'gaming'],
name: 'Alice',
};
const expected = {
hobbies: ['reading', 'gaming'],
name: 'Alice',
};
const result = flattenObject(nestedObject);
expect(result).toEqual(expected);
});
it('should flatten objects with nested arrays correctly', () => {
const nestedObject = {
person: {
hobbies: ['reading', 'gaming'],
name: 'Alice',
},
};
const expected = {
personHobbies: ['reading', 'gaming'],
personName: 'Alice',
};
const result = flattenObject(nestedObject);
expect(result).toEqual(expected);
});
it('should handle objects with undefined values', () => {
const nestedObject = {
user: {
age: undefined,
name: 'Alice',
},
};
const expected = {
userAge: undefined,
userName: 'Alice',
};
const result = flattenObject(nestedObject);
expect(result).toEqual(expected);
});
});
describe('toNestedObject', () => {
it('should convert flat object to nested object with level 1', () => {
const flatObject = {
anotherKeyExample: 2,
commonAppName: 1,
someOtherKey: 3,
};
const expectedNestedObject = {
anotherKeyExample: 2,
commonAppName: 1,
someOtherKey: 3,
};
expect(toNestedObject(flatObject, 1)).toEqual(expectedNestedObject);
});
it('should convert flat object to nested object with level 2', () => {
const flatObject = {
appAnotherKeyExample: 2,
appCommonName: 1,
appSomeOtherKey: 3,
};
const expectedNestedObject = {
app: {
anotherKeyExample: 2,
commonName: 1,
someOtherKey: 3,
},
};
expect(toNestedObject(flatObject, 2)).toEqual(expectedNestedObject);
});
it('should convert flat object to nested object with level 3', () => {
const flatObject = {
appAnotherKeyExampleValue: 2,
appCommonNameKey: 1,
appSomeOtherKeyItem: 3,
};
const expectedNestedObject = {
app: {
another: {
keyExampleValue: 2,
},
common: {
nameKey: 1,
},
some: {
otherKeyItem: 3,
},
},
};
expect(toNestedObject(flatObject, 3)).toEqual(expectedNestedObject);
});
it('should handle empty object', () => {
const flatObject = {};
const expectedNestedObject = {};
expect(toNestedObject(flatObject, 1)).toEqual(expectedNestedObject);
});
it('should handle single key object', () => {
const flatObject = {
singleKey: 1,
};
const expectedNestedObject = {
singleKey: 1,
};
expect(toNestedObject(flatObject, 1)).toEqual(expectedNestedObject);
});
it('should handle complex keys', () => {
const flatObject = {
anotherComplexKeyWithParts: 2,
complexKeyWithMultipleParts: 1,
};
const expectedNestedObject = {
anotherComplexKeyWithParts: 2,
complexKeyWithMultipleParts: 1,
};
expect(toNestedObject(flatObject, 1)).toEqual(expectedNestedObject);
});
});

View File

@@ -7,7 +7,7 @@
"repository": {
"type": "git",
"url": "git+https://github.com/vbenjs/vue-vben-admin.git",
"directory": "packages/@vben-core/shared/toolkit"
"directory": "packages/@vben-core/shared/cache"
},
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
"scripts": {

View File

@@ -7,7 +7,7 @@
"repository": {
"type": "git",
"url": "git+https://github.com/vbenjs/vue-vben-admin.git",
"directory": "packages/@vben-core/iconify"
"directory": "packages/@vben-core/shared/iconify"
},
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
"files": [

View File

@@ -1,6 +1,10 @@
import { describe, expect, it } from 'vitest';
import { capitalizeFirstLetter, toLowerCaseFirstLetter } from './letter';
import {
capitalizeFirstLetter,
toCamelCase,
toLowerCaseFirstLetter,
} from './letter';
// 编写测试用例
describe('capitalizeFirstLetter', () => {
@@ -53,3 +57,22 @@ describe('toLowerCaseFirstLetter', () => {
expect(toLowerCaseFirstLetter('123Number')).toBe('123Number');
});
});
describe('toCamelCase', () => {
it('should return the key if parentKey is empty', () => {
expect(toCamelCase('child', '')).toBe('child');
});
it('should combine parentKey and key in camel case', () => {
expect(toCamelCase('child', 'parent')).toBe('parentChild');
});
it('should handle empty key and parentKey', () => {
expect(toCamelCase('', '')).toBe('');
});
it('should handle key with capital letters', () => {
expect(toCamelCase('Child', 'parent')).toBe('parentChild');
expect(toCamelCase('Child', 'Parent')).toBe('ParentChild');
});
});

View File

@@ -17,4 +17,16 @@ function toLowerCaseFirstLetter(str: string): string {
return str.charAt(0).toLowerCase() + str.slice(1);
}
export { capitalizeFirstLetter, toLowerCaseFirstLetter };
/**
* 生成驼峰命名法的键名
* @param key
* @param parentKey
*/
function toCamelCase(key: string, parentKey: string): string {
if (!parentKey) {
return key;
}
return parentKey + key.charAt(0).toUpperCase() + key.slice(1);
}
export { capitalizeFirstLetter, toCamelCase, toLowerCaseFirstLetter };