objectUtils.ts 4.5 KB

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