SetOperations.js 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. import Utils from "../Utils.js";
  2. /**
  3. *
  4. */
  5. class SetOps {
  6. /**
  7. *
  8. */
  9. constructor() {
  10. this._sampleDelimiter = "\\n\\n";
  11. this._operation = ["Union", "Intersection", "Set Difference", "Symmetric Difference", "Cartesian Product", "Power Set"];
  12. this._itemDelimiter = ",";
  13. }
  14. /**
  15. *
  16. */
  17. get OPERATION() {
  18. return this._operation;
  19. }
  20. /**
  21. *
  22. */
  23. get SAMPLE_DELIMITER() {
  24. return this._sampleDelimiter;
  25. }
  26. /**
  27. *
  28. */
  29. get ITEM_DELIMITER() {
  30. return this._itemDelimiter;
  31. }
  32. /**
  33. *
  34. * @param {*} input
  35. * @param {*} args
  36. */
  37. runSetOperation(input, args) {
  38. const [sampleDelim, itemDelimiter, operation] = args;
  39. const sets = input.split(sampleDelim);
  40. if (!sets || (sets.length !== 2 && operation !== "Power Set") || (sets.length !== 1 && operation === "Power Set")) {
  41. return "Incorrect number of sets, perhaps you need to modify the sample delimiter or add more samples?";
  42. }
  43. if (this._operation.indexOf(operation) === -1) {
  44. return "Invalid 'Operation' option.";
  45. }
  46. let result = {
  47. Union: this.runUnion,
  48. Intersection: this.runIntersect,
  49. "Set Difference": this.runSetDifference,
  50. "Symmetric Difference": this.runSymmetricDifference,
  51. "Cartesian Product": this.runCartesianProduct,
  52. "Power Set": this.runPowerSet(itemDelimiter),
  53. }[operation]
  54. .apply(null, sets.map(s => s.split(itemDelimiter)));
  55. // Formatting issues due to the nested characteristics of power set.
  56. if (operation === "Power Set") {
  57. result = result.map(i => `${i}\n`).join("");
  58. } else {
  59. result = result.join(itemDelimiter);
  60. }
  61. return Utils.escapeHtml(result);
  62. }
  63. /**
  64. *
  65. * @param {*} a
  66. * @param {*} a
  67. */
  68. runUnion(a, b) {
  69. const result = {};
  70. /**
  71. *
  72. * @param {*} r
  73. */
  74. const addUnique = (hash) => (item) => {
  75. if (!hash[item]) {
  76. hash[item] = true;
  77. }
  78. };
  79. a.map(addUnique(result));
  80. b.map(addUnique(result));
  81. return Object.keys(result);
  82. }
  83. /**
  84. *
  85. * @param {*} a
  86. * @param {*} b
  87. */
  88. runIntersect(a, b) {
  89. return a.filter((item) => {
  90. return b.indexOf(item) > -1;
  91. });
  92. }
  93. /**
  94. *
  95. * @param {*} a
  96. * @param {*} b
  97. */
  98. runSetDifference(a, b) {
  99. return a.filter((item) => {
  100. return b.indexOf(item) === -1;
  101. });
  102. }
  103. /**
  104. *
  105. * @param {*} a
  106. * @param {*} b
  107. */
  108. runSymmetricDifference(a, b) {
  109. /**
  110. *
  111. * @param {*} refArray
  112. */
  113. const getDifference = (refArray) => (item) => {
  114. return refArray.indexOf(item) === -1;
  115. };
  116. return a
  117. .filter(getDifference(b))
  118. .concat(b.filter(getDifference(a)));
  119. }
  120. /**
  121. *
  122. * @param {*} a
  123. * @param {*} b
  124. */
  125. runCartesianProduct(a, b) {
  126. return Array(Math.max(a.length, b.length))
  127. .fill(null)
  128. .map((item, index) => `(${a[index] || undefined},${b[index] || undefined})`);
  129. }
  130. /**
  131. *
  132. * @param {*} a
  133. */
  134. runPowerSet(delimiter) {
  135. return function(a) {
  136. /**
  137. *
  138. * @param {*} dec
  139. */
  140. const toBinary = (dec) => (dec >>> 0).toString(2);
  141. const result = new Set();
  142. const maxBinaryValue = parseInt(Number(a.map(i => "1").reduce((p, c) => p + c)), 2);
  143. const binaries = [...Array(maxBinaryValue + 1).keys()]
  144. .map(toBinary)
  145. .map(i => i.padStart(toBinary(maxBinaryValue).length, "0"));
  146. binaries.forEach((binary) => {
  147. const split = binary.split("");
  148. result.add(a.filter((item, index) => split[index] === "1"));
  149. });
  150. // map for formatting & put in length order.
  151. return [...result].map(r => r.join(delimiter)).sort((a, b) => a.length - b.length);
  152. };
  153. }
  154. }
  155. export default new SetOps();