objectUtils.ts 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. /**
  2. * @category Services/utils
  3. * @class ObjectUtils
  4. * Classe aidant à manipuler des Objets
  5. */
  6. import _ from 'lodash'
  7. import type { AnyJson } from '~/types/data'
  8. import StringUtils from '~/services/utils/stringUtils'
  9. export default class ObjectUtils {
  10. // Private constructor to prevent instantiation
  11. private constructor() {
  12. // This utility class is not meant to be instantiated
  13. }
  14. /**
  15. * Flatten un objet nested en un objet avec un seul niveau avec des noms de propriétés transformées comme cela 'foo.bar'
  16. * L'objet passé en paramètre reste inchangé car il est cloné
  17. *
  18. * @example cloneAndFlatten({ a: 1, b: { c: 2 }, d: { e: 3, f: { g: 4, h: 5 } }, i: { j: 6 } }, ['i']) => { a: 1, 'b.c': 2, 'd.e': 3, 'd.f.g': 4, 'd.f.h': 5, i: { j: 6 } } }
  19. * @param {AnyJson} object
  20. * @param {Array<string>} excludedProperties
  21. * @param excludedProperties
  22. * @return {AnyJson}
  23. */
  24. static cloneAndFlatten(
  25. object: AnyJson,
  26. excludedProperties: Array<string> = [],
  27. ): AnyJson {
  28. if (typeof object !== 'object') {
  29. throw new TypeError('Expecting an object parameter')
  30. }
  31. return Object.keys(object).reduce((values: AnyJson, name: string) => {
  32. if (!Object.prototype.hasOwnProperty.call(object, name)) {
  33. return values
  34. }
  35. if (this.isObject(object[name])) {
  36. if (!excludedProperties.includes(name)) {
  37. const flatObject = this.cloneAndFlatten(object[name])
  38. Object.keys(flatObject).forEach((flatObjectKey) => {
  39. if (
  40. !Object.prototype.hasOwnProperty.call(flatObject, flatObjectKey)
  41. ) {
  42. return
  43. }
  44. values[name + '.' + flatObjectKey] = flatObject[flatObjectKey]
  45. })
  46. } else {
  47. values[name] = this.clone(object[name])
  48. }
  49. } else {
  50. values[name] = object[name]
  51. }
  52. return values
  53. }, {})
  54. }
  55. /**
  56. * Transforme un objet flattened en un objet nested. L'objet passé en paramètre reste inchangé
  57. *
  58. * @example cloneAndNest({ a: 1, 'b.c': 2, 'd.e': 3, 'd.f.g': 4, 'd.f.h': 5 } ) => { a: 1, b: { c: 2 }, d: { e: 3, f: { g: 4, h: 5 } } }
  59. * @param {AnyJson} object
  60. * @return {AnyJson}
  61. */
  62. static cloneAndNest(object: AnyJson): AnyJson {
  63. if (typeof object !== 'object') {
  64. throw new TypeError('Expecting an object parameter')
  65. }
  66. return Object.keys(object).reduce((values, name) => {
  67. if (!Object.prototype.hasOwnProperty.call(object, name)) {
  68. return values
  69. }
  70. name
  71. .split('.')
  72. .reduce(
  73. (
  74. previous: AnyJson,
  75. current: string,
  76. index: number,
  77. list: Array<string>,
  78. ) => {
  79. if (previous != null) {
  80. if (typeof previous[current] === 'undefined') {
  81. previous[current] = {}
  82. }
  83. if (index < list.length - 1) {
  84. return previous[current]
  85. }
  86. previous[current] = object[name]
  87. }
  88. return null
  89. },
  90. values,
  91. )
  92. return values
  93. }, {})
  94. }
  95. /**
  96. * Teste si le paramètre est un objet
  97. *
  98. * @param {AnyJson} value
  99. * @return {boolean}
  100. */
  101. static isObject(value: unknown): boolean {
  102. return (
  103. value !== null &&
  104. typeof value === 'object' &&
  105. !Array.isArray(value) &&
  106. ObjectUtils.prototype.toString.call(value) !== '[object Date]'
  107. )
  108. }
  109. /**
  110. * Clone l'objet et ses propriétés.
  111. *
  112. * @param {ObjectUtils} object
  113. * @return {ObjectUtils}
  114. */
  115. static clone(object: AnyJson): AnyJson {
  116. return Object.keys(object).reduce((values: AnyJson, name: string) => {
  117. if (Object.prototype.hasOwnProperty.call(object, name)) {
  118. values[name] = object[name]
  119. }
  120. return values
  121. }, {})
  122. }
  123. /**
  124. * Trie un objet selon ses clés (par ordre alphanumérique)
  125. *
  126. * @example sortObjectsByKey({b:1, d:2, c:3, a:4}) => {a:4, b:1, c:3, d:2}
  127. * @param toSort
  128. */
  129. static sortObjectsByKey(toSort: AnyJson): AnyJson {
  130. if (typeof toSort !== 'object') {
  131. throw new TypeError('Expecting an object parameter')
  132. }
  133. return Object.keys(toSort)
  134. .sort()
  135. .reduce((obj: AnyJson, key: string) => {
  136. obj[key] = toSort[key]
  137. return obj
  138. }, {})
  139. }
  140. /**
  141. * Créé un hash à partir d'un objet
  142. * (après l'avoir trié selon ses clés, et converti en json sans espace)
  143. *
  144. * @param obj
  145. */
  146. static async hash(obj: object): Promise<string> {
  147. const sortedObject = this.sortObjectsByKey(_.cloneDeep(obj))
  148. return await StringUtils.hash(JSON.stringify(sortedObject), 'SHA-1')
  149. }
  150. }