data-utils.js 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. // Data source utils
  2. /**
  3. * Extracts unique values of a specified nested property from an array of JSON objects.
  4. *
  5. * @param {Array} data - An array of JSON objects from which to extract unique values.
  6. * @param {string} key - The dot-separated key representing the nested property (e.g., 'source.id').
  7. * @returns {Array} An array containing the unique values of the specified nested property.
  8. *
  9. * @example
  10. * const data = [
  11. * {"id": 1, "name": "foo", "source": {"id": 4, "name": "bar"}},
  12. * {"id": 2, "name": "baz", "source": {"id": 4, "name": "qux"}},
  13. * {"id": 3, "name": "quux", "source": {"id": 5, "name": "corge"}}
  14. * ];
  15. *
  16. * const key = 'source.id';
  17. * const uniqueSourceIds = getUniqueValues(data, key);
  18. * console.log(uniqueSourceIds); // Output: [4, 5]
  19. */
  20. export function getUniqueValues(data, key) {
  21. var lookup = {};
  22. var items = data;
  23. var results = [];
  24. for (var item, i = 0; item = items[i++];) {
  25. var val = getValueByNestedKey(item, key);
  26. if (!(val in lookup)) {
  27. lookup[val] = 1;
  28. results.push(val);
  29. }
  30. }
  31. return results;
  32. }
  33. /**
  34. * Retrieves the value of a nested property in an object using a dot-separated key.
  35. *
  36. * @param {Object} obj - The input JavaScript object from which to retrieve the nested value.
  37. * @param {string} key - The dot-separated key representing the nested property (e.g., 'source.id').
  38. * @returns {*} The value of the nested property if found, otherwise, returns undefined.
  39. *
  40. * @example
  41. * const jsonString = '{"id":11,"name":"ajax","subject":"OR","mark":63,"source":{"id":4,"name":"foo"}}';
  42. * const jsonObject = JSON.parse(jsonString);
  43. *
  44. * const key = 'source.id';
  45. * const sourceId = getValueByNestedKey(jsonObject, key);
  46. * console.log(sourceId); // Output: 4
  47. */
  48. function getValueByNestedKey(obj, key) {
  49. const keys = key.split('.');
  50. let value = obj;
  51. for (const k of keys) {
  52. if (value[k] === undefined) {
  53. return undefined; // Property not found
  54. }
  55. value = value[k];
  56. }
  57. return value;
  58. }
  59. // From https://stackoverflow.com/a/49332027/13775459
  60. function toISOLocal(d) {
  61. var z = n => ('0' + n).slice(-2);
  62. var zz = n => ('00' + n).slice(-3);
  63. var off = d.getTimezoneOffset();
  64. var sign = off > 0? '-' : '+';
  65. off = Math.abs(off);
  66. return d.getFullYear() + '-' +
  67. z(d.getMonth()+1) + '-' +
  68. z(d.getDate()) + 'T' +
  69. z(d.getHours()) + ':' +
  70. z(d.getMinutes()) + ':' +
  71. z(d.getSeconds()) + '.' +
  72. zz(d.getMilliseconds()) +
  73. sign + z(off/60|0) + ':' + z(off%60);
  74. }
  75. // Create a function to convert data to CSV
  76. export function convertToCSV(data) {
  77. if (data.length === 0) {
  78. return "";
  79. }
  80. // Extract column names from the first object in the data array
  81. const columns = Object.keys(data[0]);
  82. // Create the header row
  83. const headerRow = columns.join(',') + '\n';
  84. // Create the data rows
  85. const dataRows = data.map(row => {
  86. const rowData = columns.map(col => {
  87. const value = row[col];
  88. if (typeof value === 'object' && value !== null) {
  89. return value.description || '';
  90. } else if (col === 'event_start' || col === 'belief_time') {
  91. // Check if the column is a timestamp column
  92. const timestamp = parseInt(value);
  93. if (!isNaN(timestamp)) {
  94. const date = new Date(timestamp); // Convert to Date
  95. // Format the date in ISO8601 format and localize to the specified timezone
  96. // return date.toISOString(); // Use this instead of toISOLocal to get UTC instead
  97. return toISOLocal(date);
  98. }
  99. } else if (col === 'belief_horizon') {
  100. // Check if the column is 'belief_horizon' (duration in ms)
  101. const durationMs = parseInt(value);
  102. if (!isNaN(durationMs)) {
  103. // Check if the duration is zero
  104. if (durationMs === 0) {
  105. return 'PT0H';
  106. }
  107. // Check if the duration is negative
  108. const isNegative = durationMs < 0;
  109. // Calculate absolute duration in seconds
  110. const absDurationSeconds = Math.abs(durationMs) / 1000;
  111. // Calculate hours, minutes, and seconds
  112. const hours = Math.floor(absDurationSeconds / 3600);
  113. const minutes = Math.floor((absDurationSeconds % 3600) / 60);
  114. const seconds = Math.floor(absDurationSeconds % 60);
  115. // Format the duration as ISO8601 duration
  116. let iso8601Duration = isNegative ? '-PT' : 'PT';
  117. if (hours > 0) {
  118. iso8601Duration += hours + 'H';
  119. }
  120. if (minutes > 0) {
  121. iso8601Duration += minutes + 'M';
  122. }
  123. if (seconds > 0) {
  124. iso8601Duration += seconds + 'S';
  125. }
  126. return iso8601Duration;
  127. }
  128. }
  129. return value;
  130. });
  131. return rowData.join(',');
  132. });
  133. // Combine the header row and data rows
  134. return "data:text/csv;charset=utf-8," + headerRow + dataRows.join('\n');
  135. }