/src/utils/operator.js

https://github.com/ivmartel/dwv · JavaScript · 220 lines · 138 code · 7 blank · 75 comment · 42 complexity · f041b6d4049b7653b26a5fec39ded34a MD5 · raw file

  1. // namespaces
  2. var dwv = dwv || {};
  3. dwv.utils = dwv.utils || {};
  4. /**
  5. * Check if the input is a generic object, including arrays.
  6. *
  7. * @param {*} unknown The input to check.
  8. * @returns {boolean} True if the input is an object.
  9. * ref: https://github.com/jashkenas/underscore/blob/1.9.1/underscore.js#L1319-L1323
  10. */
  11. dwv.utils.isObject = function (unknown) {
  12. var type = typeof unknown;
  13. return type === 'function' || type === 'object' && !!unknown;
  14. };
  15. /**
  16. * Check if the input is an array.
  17. *
  18. * @param {*} unknown The input to check.
  19. * @returns {boolean} True if the input is an array.
  20. * ref: https://github.com/jashkenas/underscore/blob/1.9.1/underscore.js#L1313-L1317
  21. */
  22. dwv.utils.isArray = function (unknown) {
  23. return Array.isArray(unknown);
  24. };
  25. /**
  26. * Dump an object to an array.
  27. *
  28. * @param {object} obj The input object as: {key0: {}, key1: {}}
  29. * @returns {Array} The corresponding array:
  30. * [{name: key0, {}}, {name: key1, {}}]
  31. */
  32. dwv.utils.objectToArray = function (obj) {
  33. var array = [];
  34. var keys = Object.keys(obj);
  35. for (var i = 0; i < keys.length; ++i) {
  36. var key = keys[i];
  37. var row = {name: key};
  38. var innerKeys = Object.keys(obj[key]);
  39. for (var j = 0; j < innerKeys.length; ++j) {
  40. var innerKey = innerKeys[j];
  41. var value = obj[key][innerKey];
  42. if (dwv.utils.isArray(value)) {
  43. var arrayValues = [];
  44. for (var k = 0; k < value.length; ++k) {
  45. if (dwv.utils.isObject(value[k])) {
  46. arrayValues.push(dwv.utils.objectToArray(value[k]));
  47. } else {
  48. arrayValues.push(value[k]);
  49. }
  50. }
  51. value = arrayValues;
  52. } else if (dwv.utils.isObject(value)) {
  53. value = dwv.utils.objectToArray(value);
  54. }
  55. row[innerKey] = value;
  56. }
  57. array.push(row);
  58. }
  59. return array;
  60. };
  61. /**
  62. * Merge two similar objects.
  63. * Objects need to be in the form of:
  64. * <code>
  65. * {
  66. * idKey: {valueKey: 0},
  67. * key0: {valueKey: "abc"},
  68. * key1: {valueKey: 33}
  69. * }
  70. * </code>
  71. * Merged objects will be in the form of:
  72. * <code>
  73. * {
  74. * idKey: {valueKey: [0,1,2], merged: true},
  75. * key0: {valueKey: {
  76. * 0: {valueKey: "abc"},
  77. * 1: {valueKey: "def"},
  78. * 2: {valueKey: "ghi"}
  79. * }},
  80. * key1: {valueKey: {
  81. * 0: {valueKey: 33},
  82. * 1: {valueKey: 44},
  83. * 2: {valueKey: 55}
  84. * }}
  85. * }
  86. * </code>
  87. *
  88. * @param {object} obj1 The first object, can be the result of a previous merge.
  89. * @param {object} obj2 The second object.
  90. * @param {string} idKey The key to use as index for duplicate values.
  91. * @param {string} valueKey The key to use to access object values.
  92. * @returns {object} The merged object.
  93. */
  94. dwv.utils.mergeObjects = function (obj1, obj2, idKey, valueKey) {
  95. var res = {};
  96. // check id key
  97. if (!idKey) {
  98. throw new Error('Cannot merge object with an undefined id key: ' + idKey);
  99. } else {
  100. if (!Object.prototype.hasOwnProperty.call(obj1, idKey)) {
  101. throw new Error('Id key not found in first object while merging: ' +
  102. idKey + ', obj: ' + obj1);
  103. }
  104. if (!Object.prototype.hasOwnProperty.call(obj2, idKey)) {
  105. throw new Error('Id key not found in second object while merging: ' +
  106. idKey + ', obj: ' + obj2);
  107. }
  108. }
  109. // check value key
  110. if (!valueKey) {
  111. throw new Error('Cannot merge object with an undefined value key: ' +
  112. valueKey);
  113. }
  114. // check if merged object
  115. var mergedObj1 = false;
  116. if (Object.prototype.hasOwnProperty.call(obj1[idKey], 'merged') &&
  117. obj1[idKey].merged) {
  118. mergedObj1 = true;
  119. }
  120. // handle the id part
  121. if (!Object.prototype.hasOwnProperty.call(obj1[idKey], valueKey)) {
  122. throw new Error('Id value not found in first object while merging: ' +
  123. idKey + ', valueKey: ' + valueKey + ', ojb: ' + obj1);
  124. }
  125. if (!Object.prototype.hasOwnProperty.call(obj2[idKey], valueKey)) {
  126. throw new Error('Id value not found in second object while merging: ' +
  127. idKey + ', valueKey: ' + valueKey + ', ojb: ' + obj2);
  128. }
  129. var id1 = obj1[idKey][valueKey];
  130. var id2 = obj2[idKey][valueKey];
  131. // for merged object, id1 is an array
  132. if (mergedObj1) {
  133. // check if array does not include id2
  134. for (var k = 0; k < id1.length; ++k) {
  135. if (id1[k] === id2) {
  136. throw new Error('The first object already contains id2: ' +
  137. id2 + ', id1: ' + id1);
  138. }
  139. }
  140. res[idKey] = obj1[idKey];
  141. res[idKey][valueKey].push(id2);
  142. } else {
  143. if (id1 === id2) {
  144. throw new Error('Cannot merge object with same ids: ' +
  145. id1 + ', id2: ' + id2);
  146. }
  147. // create merge object
  148. res[idKey] = {value: [id1, id2], merged: true};
  149. }
  150. // get keys
  151. var keys1 = Object.keys(obj1);
  152. // keys2 without duplicates of keys1
  153. var keys2 = Object.keys(obj2).filter(function (item) {
  154. return keys1.indexOf(item) < 0;
  155. });
  156. var keys = keys1.concat(keys2);
  157. // loop through keys
  158. for (var i = 0, leni = keys.length; i < leni; ++i) {
  159. var key = keys[i];
  160. if (key !== idKey) {
  161. // first
  162. var value1 = null;
  163. var subValue1 = null;
  164. if (Object.prototype.hasOwnProperty.call(obj1, key)) {
  165. value1 = obj1[key];
  166. if (Object.prototype.hasOwnProperty.call(value1, valueKey)) {
  167. subValue1 = value1[valueKey];
  168. }
  169. }
  170. // second
  171. var value2 = null;
  172. var subValue2 = null;
  173. if (Object.prototype.hasOwnProperty.call(obj2, key)) {
  174. value2 = obj2[key];
  175. if (Object.prototype.hasOwnProperty.call(value2, valueKey)) {
  176. subValue2 = value2[valueKey];
  177. }
  178. }
  179. // result value
  180. var value;
  181. // create merge object if different values
  182. if (subValue2 !== subValue1) {
  183. value = {};
  184. // add to merged object or create new
  185. if (mergedObj1) {
  186. if (dwv.utils.isObject(subValue1)) {
  187. value[valueKey] = subValue1;
  188. } else {
  189. // merged object with repeated value
  190. // copy it with the index list
  191. value[valueKey] = {};
  192. for (var j = 0; j < id1.length; j++) {
  193. value[valueKey][id1[j]] = value1;
  194. }
  195. }
  196. // add obj2 value
  197. value[valueKey][id2] = value2;
  198. } else {
  199. // create merge object
  200. var newValue = {};
  201. newValue[id1] = value1;
  202. newValue[id2] = value2;
  203. value[valueKey] = newValue;
  204. }
  205. } else {
  206. value = value1;
  207. }
  208. // store value in result object
  209. res[key] = value;
  210. }
  211. }
  212. return res;
  213. };