|
| 1 | +const arr = [6.1, 2.4, 2.7, 6.8]; |
| 2 | + |
| 3 | +function groupBy(arr, property) { |
| 4 | + const groupByString = (arr, query) => { |
| 5 | + let res = {}; |
| 6 | + |
| 7 | + arr.forEach((item, index) => { |
| 8 | + const queryList = query.split("."); |
| 9 | + |
| 10 | + let currKey; |
| 11 | + let currItem = item; |
| 12 | + let count = 0; |
| 13 | + for (let i = 0; i < queryList.length; i++) { |
| 14 | + currKey = queryList[i]; |
| 15 | + if (!currItem[currKey]) { |
| 16 | + break; |
| 17 | + } else { |
| 18 | + count += 1; |
| 19 | + currItem = currItem[currKey]; |
| 20 | + } |
| 21 | + } |
| 22 | + |
| 23 | + if (count !== queryList.length) { |
| 24 | + currItem = undefined; |
| 25 | + } |
| 26 | + if (!res[currItem]) { |
| 27 | + res[currItem] = [item]; |
| 28 | + } else { |
| 29 | + res[currItem].push(item); |
| 30 | + } |
| 31 | + }); |
| 32 | + return res; |
| 33 | + }; |
| 34 | + const groupByCallback = (arr, cb) => { |
| 35 | + let res = {}; |
| 36 | + arr.forEach((item) => { |
| 37 | + const ans = cb.call(null, item); |
| 38 | + |
| 39 | + if (Object.keys(res).includes(ans.toString())) { |
| 40 | + res[ans].push(item); |
| 41 | + } else { |
| 42 | + res[ans] = [item]; |
| 43 | + } |
| 44 | + }); |
| 45 | + return res; |
| 46 | + }; |
| 47 | + |
| 48 | + let res; |
| 49 | + switch (typeof property) { |
| 50 | + case "function": |
| 51 | + res = groupByCallback(arr, property); |
| 52 | + break; |
| 53 | + case "string": |
| 54 | + res = groupByString(arr, property); |
| 55 | + break; |
| 56 | + default: |
| 57 | + return new Error("Invalid Property Type"); |
| 58 | + } |
| 59 | + |
| 60 | + return res; |
| 61 | +} |
| 62 | + |
| 63 | +// groupBy(arr, Math.floor); |
| 64 | +console.log(groupBy(arr, Math.ceil)); |
| 65 | +console.log(groupBy([6.1, 4.2, 6.3], Math.floor)); |
| 66 | +console.log(groupBy(["one", "two", "three"], "length")); |
| 67 | +console.log( |
| 68 | + groupBy( |
| 69 | + [{ a: { b: { c: 1 } } }, { a: { b: { c: 1 } } }, { a: { b: { c: 2 } } }], |
| 70 | + "b.a.c" |
| 71 | + ) |
| 72 | +); |
| 73 | + |
| 74 | +console.log( |
| 75 | + groupBy( |
| 76 | + [{ a: { b: { c: 1 } } }, { a: { b: { c: 1 } } }, { a: { b: { c: 2 } } }], |
| 77 | + "a.b.c" |
| 78 | + ) |
| 79 | +); |
| 80 | + |
| 81 | +// alternate solution |
| 82 | +function groupByAlternative(collection, property) { |
| 83 | + const output = {}; |
| 84 | + |
| 85 | + if (!collection || typeof collection !== "object") { |
| 86 | + return output; |
| 87 | + } |
| 88 | + |
| 89 | + const isPropertyFunction = typeof property === "function"; |
| 90 | + const isPropertyPath = typeof property === "string"; |
| 91 | + |
| 92 | + for (const value of Object.values(collection)) { |
| 93 | + let current = undefined; |
| 94 | + |
| 95 | + if (isPropertyFunction) { |
| 96 | + current = property(value); |
| 97 | + } else if (isPropertyPath) { |
| 98 | + // a.b.c -> [a, b, c]; |
| 99 | + const path = property.split("."); |
| 100 | + let i; |
| 101 | + let currentKey; |
| 102 | + let currentItem = value; // { a: { b: { c: 1 } } } |
| 103 | + |
| 104 | + for (i = 0; i < path.length; i++) { |
| 105 | + // [a, b, c] -> currentKey: path[0] -> a |
| 106 | + // [a, b, c] -> currentKey: path[1] -> b |
| 107 | + // [a, b, c] -> currentKey: path[2] -> c |
| 108 | + currentKey = path[i]; |
| 109 | + |
| 110 | + if (!currentItem[currentKey]) { |
| 111 | + currentItem = undefined; |
| 112 | + break; |
| 113 | + } |
| 114 | + currentItem = currentItem[currentKey]; |
| 115 | + } |
| 116 | + |
| 117 | + current = currentItem; |
| 118 | + } |
| 119 | + |
| 120 | + output[current] = output[current] || []; |
| 121 | + output[current].push(value); |
| 122 | + } |
| 123 | + |
| 124 | + return output; |
| 125 | +} |
0 commit comments