|
1 | | -import { objectify, isObject, isArray, get } from 'radash'; |
2 | | -import { ClassTransformOptions, instanceToPlain } from 'class-transformer'; |
| 1 | +import { objectify, isObject, isArray, get } from 'radash' |
| 2 | +import { ClassTransformOptions, instanceToPlain } from 'class-transformer' |
3 | 3 |
|
| 4 | +/** |
| 5 | + * Extrait récursivement tous les chemins de propriétés d'un objet sous forme de notation à points. |
| 6 | + * |
| 7 | + * Parcourt un objet ou tableau de manière récursive pour générer la liste complète |
| 8 | + * des chemins d'accès aux propriétés en notation pointée (ex: "user.address.city"). |
| 9 | + * |
| 10 | + * @template TValue Type de l'objet à analyser |
| 11 | + * @param {TValue} value - Objet dont extraire les chemins de propriétés |
| 12 | + * @returns {string[]} Tableau des chemins en notation pointée |
| 13 | + * |
| 14 | + * @description |
| 15 | + * Comportement selon le type de données : |
| 16 | + * - Objet : Parcourt récursivement toutes les propriétés |
| 17 | + * - Tableau de primitives : Retourne les chemins sans indices |
| 18 | + * - Tableau d'objets : Inclut les indices numériques dans les chemins |
| 19 | + * - Valeur primitive : Retourne le chemin complet |
| 20 | + * - Valeur nulle/undefined : Retourne un tableau vide |
| 21 | + * |
| 22 | + * @example |
| 23 | + * ```typescript |
| 24 | + * const data = { |
| 25 | + * user: { |
| 26 | + * name: 'John', |
| 27 | + * address: { |
| 28 | + * city: 'Paris', |
| 29 | + * zip: '75001' |
| 30 | + * } |
| 31 | + * }, |
| 32 | + * tags: ['admin', 'user'] |
| 33 | + * }; |
| 34 | + * |
| 35 | + * const paths = keys(data); |
| 36 | + * // Retourne: [ |
| 37 | + * // 'user.name', |
| 38 | + * // 'user.address.city', |
| 39 | + * // 'user.address.zip', |
| 40 | + * // 'tags' |
| 41 | + * // ] |
| 42 | + * |
| 43 | + * const arrayData = { |
| 44 | + * items: [ |
| 45 | + * { id: 1, name: 'Item 1' }, |
| 46 | + * { id: 2, name: 'Item 2' } |
| 47 | + * ] |
| 48 | + * }; |
| 49 | + * |
| 50 | + * const arrayPaths = keys(arrayData); |
| 51 | + * // Retourne: [ |
| 52 | + * // 'items.0.id', |
| 53 | + * // 'items.0.name', |
| 54 | + * // 'items.1.id', |
| 55 | + * // 'items.1.name' |
| 56 | + * // ] |
| 57 | + * ``` |
| 58 | + */ |
4 | 59 | export const keys = <TValue extends object>(value: TValue): string[] => { |
5 | | - if (!value) return []; |
| 60 | + if (!value) return [] |
| 61 | + |
6 | 62 | const getKeys = (nested: any, paths: string[]): string[] => { |
7 | 63 | if (isObject(nested)) { |
8 | | - return Object.entries(nested).flatMap(([k, v]) => getKeys(v, [...paths, k])); |
| 64 | + return Object.entries(nested).flatMap(([k, v]) => getKeys(v, [...paths, k])) |
9 | 65 | } |
| 66 | + |
10 | 67 | if (isArray(nested)) { |
11 | 68 | if (nested.length > 0 && ['string', 'number', 'boolean'].includes(typeof nested[0])) { |
12 | | - return nested.flatMap((item) => getKeys(item, paths)); |
| 69 | + return nested.flatMap((item) => getKeys(item, paths)) |
13 | 70 | } |
14 | | - return nested.flatMap((item, i) => getKeys(item, [...paths, `${i}`])); |
| 71 | + |
| 72 | + return nested.flatMap((item, i) => getKeys(item, [...paths, `${i}`])) |
15 | 73 | } |
16 | | - return [paths.join('.')]; |
17 | | - }; |
18 | | - return getKeys(value, []); |
19 | | -}; |
20 | 74 |
|
| 75 | + return [paths.join('.')] |
| 76 | + } |
| 77 | + |
| 78 | + return getKeys(value, []) |
| 79 | +} |
| 80 | + |
| 81 | +/** |
| 82 | + * Convertit un objet ou une instance de classe en objet plat avec notation pointée. |
| 83 | + * |
| 84 | + * Cette fonction transforme d'abord l'objet en plain object (via class-transformer), |
| 85 | + * puis "écrase" la structure imbriquée en un objet plat où les clés utilisent |
| 86 | + * la notation à points pour représenter les chemins d'accès. |
| 87 | + * |
| 88 | + * @template TValue Type de l'objet à transformer |
| 89 | + * @param {TValue} value - Objet ou instance de classe à aplatir |
| 90 | + * @param {ClassTransformOptions} [options] - Options de transformation class-transformer |
| 91 | + * @returns {object} Objet plat avec clés en notation pointée |
| 92 | + * |
| 93 | + * @description |
| 94 | + * Processus de transformation : |
| 95 | + * 1. Conversion de l'instance en plain object (supprime les méthodes, applique @Expose/@Exclude) |
| 96 | + * 2. Extraction de tous les chemins de propriétés |
| 97 | + * 3. Création d'un nouvel objet avec clés en notation pointée |
| 98 | + * |
| 99 | + * Cas d'usage typiques : |
| 100 | + * - Préparation de données pour des systèmes de recherche |
| 101 | + * - Export de données en format CSV/Excel |
| 102 | + * - Indexation dans des bases de données NoSQL |
| 103 | + * - Comparaison de structures complexes |
| 104 | + * |
| 105 | + * @example |
| 106 | + * ```typescript |
| 107 | + * class User { |
| 108 | + * name: string; |
| 109 | + * address: { |
| 110 | + * city: string; |
| 111 | + * zip: string; |
| 112 | + * }; |
| 113 | + * } |
| 114 | + * |
| 115 | + * const user = new User(); |
| 116 | + * user.name = 'John Doe'; |
| 117 | + * user.address = { city: 'Paris', zip: '75001' }; |
| 118 | + * |
| 119 | + * const flat = toPlainAndCrush(user); |
| 120 | + * // Retourne: { |
| 121 | + * // 'name': 'John Doe', |
| 122 | + * // 'address.city': 'Paris', |
| 123 | + * // 'address.zip': '75001' |
| 124 | + * // } |
| 125 | + * |
| 126 | + * // Avec options class-transformer |
| 127 | + * const flatExcluded = toPlainAndCrush(user, { |
| 128 | + * excludeExtraneousValues: true |
| 129 | + * }); |
| 130 | + * |
| 131 | + * // Utilisation pour la recherche |
| 132 | + * const searchIndex = toPlainAndCrush(complexObject); |
| 133 | + * Object.entries(searchIndex).forEach(([key, value]) => { |
| 134 | + * indexer.add(key, value); |
| 135 | + * }); |
| 136 | + * ``` |
| 137 | + */ |
21 | 138 | export const toPlainAndCrush = <TValue extends object>(value: TValue, options?: ClassTransformOptions): object => { |
22 | 139 | return objectify( |
23 | 140 | keys(instanceToPlain(value, options)), |
24 | 141 | (k) => k, |
25 | 142 | (k) => get(value, k), |
26 | | - ); |
27 | | -}; |
| 143 | + ) |
| 144 | +} |
0 commit comments