identical-links-same-purpose-after.js 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. /**
  2. * Check if two given objects are the same (Note: this fn is not extensive in terms of depth equality)
  3. * @param {Object} a object a, to compare
  4. * @param {*} b object b, to compare
  5. * @returns {Boolean}
  6. */
  7. function isIdenticalObject(a, b) {
  8. if (!a || !b) {
  9. return false;
  10. }
  11. const aProps = Object.getOwnPropertyNames(a);
  12. const bProps = Object.getOwnPropertyNames(b);
  13. if (aProps.length !== bProps.length) {
  14. return false;
  15. }
  16. const result = aProps.every(propName => {
  17. const aValue = a[propName];
  18. const bValue = b[propName];
  19. if (typeof aValue !== typeof bValue) {
  20. return false;
  21. }
  22. if (typeof aValue === `object` || typeof bValue === `object`) {
  23. return isIdenticalObject(aValue, bValue);
  24. }
  25. return aValue === bValue;
  26. });
  27. return result;
  28. }
  29. function identicalLinksSamePurposeAfter(results) {
  30. /**
  31. * Skip, as no results to curate
  32. */
  33. if (results.length < 2) {
  34. return results;
  35. }
  36. /**
  37. * Filter results for which `result` is undefined & thus `data`, `relatedNodes` are undefined
  38. */
  39. const incompleteResults = results.filter(
  40. ({ result }) => result !== undefined
  41. );
  42. /**
  43. * for each result
  44. * - get other results with matching accessible name
  45. * - check if same purpose is served
  46. * - if not change `result` to `undefined`
  47. * - construct a list of unique results with relatedNodes to return
  48. */
  49. const uniqueResults = [];
  50. const nameMap = {};
  51. for (let index = 0; index < incompleteResults.length; index++) {
  52. const currentResult = incompleteResults[index];
  53. const { name, urlProps } = currentResult.data;
  54. /**
  55. * This is to avoid duplications in the `nodeMap`
  56. */
  57. if (nameMap[name]) {
  58. continue;
  59. }
  60. const sameNameResults = incompleteResults.filter(
  61. ({ data }, resultNum) => data.name === name && resultNum !== index
  62. );
  63. const isSameUrl = sameNameResults.every(({ data }) =>
  64. isIdenticalObject(data.urlProps, urlProps)
  65. );
  66. /**
  67. * when identical nodes exists but do not resolve to same url, flag result as `incomplete`
  68. */
  69. if (sameNameResults.length && !isSameUrl) {
  70. currentResult.result = undefined;
  71. }
  72. /**
  73. * -> deduplicate results (for both `pass` or `incomplete`) and add `relatedNodes` if any
  74. */
  75. currentResult.relatedNodes = [];
  76. currentResult.relatedNodes.push(
  77. ...sameNameResults.map(node => node.relatedNodes[0])
  78. );
  79. /**
  80. * Update `nodeMap` with `sameNameResults`
  81. */
  82. nameMap[name] = sameNameResults;
  83. uniqueResults.push(currentResult);
  84. }
  85. return uniqueResults;
  86. }
  87. export default identicalLinksSamePurposeAfter;