I'd like to re-factor Cache class purely for academic reason.
However, I'm having a tough time figuring out how I can move getMap out of the Cache class and have it as a regular function.
function isObject(arg) {
const typeOfObj = typeof arg;
return (typeOfObj === 'object' || typeOfObj === 'function') && arg !== null;
}
class Cache {
constructor() {
this.map = new Map();
this.weakmap = new WeakMap();
}
// return a Cache's value at a key
getMap(key) {
// console.log(this);
const map = this[isObject(key) ? 'weakmap' : 'map'];
// console.log(map);
this.setKeyIfNeeded(map, key);
let valueMap = map.get(key);
return valueMap;
}
// create a Cache's key, if needed
setKeyIfNeeded(map, key) {
if (!map.has(key)) {
map.set(key, new Cache());
}
}
}
const getNestedMap = (keys, initialCache) =>
keys.reduce((cache, key) => cache.getMap(key), initialCache);
function memoize(fn) {
const cache = new Cache();
return (...args) => {
// get (or create) a cache item
const item = getNestedMap(args, cache);
if (Reflect.has(item, 'value')) {
return item.value;
}
return (item.value = fn(args));
};
}
let counter = 1;
function foo() {
counter += 1;
return counter;
}
const id1 = Symbol('id');
const id2 = Symbol('id');
const memoizedFoo = memoize(foo);
console.log(memoizedFoo(3, 4, 5, 6)); //2
console.log(memoizedFoo(3, 4, 5, 6)); //2
console.log(memoizedFoo(3, 4, 6)); //3
console.log(memoizedFoo(3, 4, 6)); //3
You will need to rewrite your function to accept all things. Then you'd just call it from within your class.
One example might be:
getMap(cache, key, isObject, setKeyIfNeeded) {
// console.log(this);
const map = cache[isObject(key) ? 'weakmap' : 'map'];
// console.log(map);
setKeyIfNeeded(map, key);
let valueMap = map.get(key);
return valueMap;
}
SOLUTION
function isObject(arg) {
const typeOfObj = typeof arg;
return (typeOfObj === 'object' || typeOfObj === 'function') && arg !== null;
}
class Cache {
constructor() {
this.map = new Map();
this.weakmap = new WeakMap();
}
static setKey(key, map) {
return map.set(key, new Cache());
}
}
function getCache(args, cache) {
for (const key of args) {
const map = cache[isObject(key) ? 'weakmap' : 'map'];
cache = map.get(key) || Cache.setKey(key, map).get(key);
}
return cache;
}
function memoize(fn) {
const cache = new Cache();
return (...args) => {
const item = getCache(args, cache);
if (Reflect.has(item, 'value')) {
return item.value;
}
return (item.value = fn(args));
};
}
let counter = 1;
function foo() {
counter += 1;
return counter;
}
Related
I'm trying to figure out a way to turn and object like this :
{ "test.subtest.pass" : "test passed", "test.subtest.fail" : "test failed" }
into JSON like this:
{ "test": { "subtest": { "pass": "test passed", "fail": "test failed" }}}
sometimes there may be duplicate keys, as above perhaps there would be another entry like "test.subtest.pass.mark"
I have tried using the following method and it works but it's incredibly ugly:
convertToJSONFormat() {
const objectToTranslate = require('<linkToFile>');
const resultMap = this.objectMap(objectToTranslate, (item: string) => item.split('.'));
let newMap:any = {};
for (const [key,value] of Object.entries(resultMap)) {
let previousValue = null;
// #ts-ignore
for (const item of value) {
// #ts-ignore
if (value.length === 1) {
if(!newMap.hasOwnProperty(item)) {
newMap[item] = key
} // #ts-ignore
} else if (item === value[value.length - 1]) {
if(typeof previousValue[item] === 'string' ) {
const newKey = previousValue[item].toLowerCase().replace(/\s/g, '');;
const newValue = previousValue[item];
previousValue[item] = {};
previousValue[item][newKey] = newValue;
previousValue[item][item] = key;
} else {
previousValue[item] = key;
}
} else if (previousValue === null) {
if (!newMap.hasOwnProperty(item)) {
newMap[item] = {};
}
previousValue = newMap[item];
} else {
if (!previousValue.hasOwnProperty(item)) {
previousValue[item] = {}
previousValue = previousValue[item];
} else if (typeof previousValue[item] === 'string') {
const newValue = previousValue[item];
previousValue[item] = {};
previousValue[item][item] = newValue;
} else {
previousValue = previousValue[item];
}
}
}
}
return newMap;
}
We can utilize recursion to make the code a little less verbose:
function convertToJSONFormat(objectToTranslate) {
// create root object for the conversion result
const result = {};
// iterate each key-value pair on the object to be converted
Object
.entries(objectToTranslate)
.forEach(([path, value]) => {
// utilize a recursive function to write the value into the result object
addArrayPathToObject(result, path.split("."), value);
});
return result;
}
function addArrayPathToObject(root, parts, value) {
const p = parts.shift();
// base-case: We attach the value if we reach the last path fragment
if (parts.length == 0) {
root[p] = value
return;
}
// general case: check if root[p] exists, otherwise create it and set as new root.
if(!root[p]) root[p] = {};
addArrayPathToObject(root[p], parts, value)
}
This function utilizes the fact that objects are pass-by-reference to recursively traverse through the object starting at its root until setting the desired value.
You can add error-handling and other such concerns as necessary for your use.
#Meggan Naude, toJson function copies json object to reference obj for provided keys and value.
const p = { "test.subtest.pass" : "test passed", "test.subtest.fail" : "test failed" };
const result = {} ;
const toJson = (obj, keys, value) => {
if (keys?.length === 1) {
obj[keys[0]] = value;
return obj
} else {
const k = keys.splice(0, 1)
if (k in obj) {
toJson(obj[k], keys, value)
} else {
obj[k] = {};
toJson(obj[k], keys, value)
}
return obj
}
}
Object.keys(p).forEach(key => toJson(result, key.split('.'), p[key]))
console.log(result);
Input: 5 -> 9 -> 8 -> 3 -> 1 -> 7
Expected Output: 7 -> 1 -> 3 -> 8 -> 9 -> 5
Issue:
When I display the reversed linked list the result is 5. This is an issue because this should be the tail and not the head. The rest of the nodes are missing in the display well.
Question:
Is there an issue with the code base that is preventing the traversal from the head to the tail and changing the pointers to reverse the list?
Code:
LinkedList:
class LinkedList {
constructor() {
this.head = null;
this.size = 0;
}
insertFirst(item) {
if (this.head !== null) {
const newHead = new _Node(item);
let oldHead = this.head;
oldHead.prev = newHead;
newHead.next = oldHead;
this.head = newHead;
} else {
this.head = new _Node(item, this.head);
}
this.size++;
}
insertLast(item) {
if (!this.head) {
this.insertFirst(item);
} else {
let tempNode = this.head;
while (tempNode.next !== null) {
tempNode = tempNode.next;
}
tempNode.next = new _Node(item, null, tempNode);
}
this.size++
}
}
module.exports = LinkedList;
Main:
const LinkedList = require("./LinkedLists");
const { reverse } = require("./Reverse");
const { display } = require("./Supplemental");
function main() {
let SLL = new LinkedList();
SLL.insertFirst(5);
SLL.insertLast(9);
SLL.insertLast(8);
SLL.insertLast(3);
SLL.insertLast(1);
SLL.insertLast(7);
reverse(SLL);
display(SLL);
return SLL;
}
console.log(main());
Reverse:
reverse = (SLL) => {
let curr = SLL.head
if (!curr) {
return;
}
if (!curr.next) {
SLL.head = curr;
return;
}
let tmp = reverse(curr.next);
curr.next.next = curr;
curr.next = null;
return tmp;
}
module.exports = { reverse };
Display:
display = (SLL) => {
let currentNode = SLL.head;
if (!SLL.head) {
return null;
}
while (currentNode !== null) {
console.log(currentNode.value);
currentNode = currentNode.next;
}
return;
};
Can someone tell me what return tmp is doing in the Reverse.js file?
(1) Removed display from Main.js
(2) Edited Main.js:
const LinkedList = require("./LinkedLists");
const { reverse } = require("./Reverse");
const { display } = require("./Supplemental");
function main() {
let SLL = new LinkedList();
SLL.insertFirst(5);
SLL.insertLast(9);
SLL.insertLast(8);
SLL.insertLast(3);
SLL.insertLast(1);
SLL.insertLast(7);
const result = reverse(SLL.head);
console.log(result);
return SLL;
}
return main();
(3) Edited Reverse.js:
reverse = (curr, prev = null) => {
if (!curr) {
return prev;
}
let tmp = reverse(curr.next, curr);
const temp = curr.next;
curr.next = prev;
curr.prev = temp;
return tmp;
}
module.exports = { reverse };
Is there a way to chain the following methods from Cache class
cache = cache.getKey(key) || cache.setKey(key).get(key);
// cache = cache.getKey(key) || cache.setKey(key).getKey(key);
I know we have native methods Map.prototype.set() and Map.prototype.get() but I want to implement it this way. Let me know if you have any suggestions.
function isObject(arg) {
const typeOfObj = typeof arg;
return (typeOfObj === 'object' || typeOfObj === 'function') && arg !== null;
}
class Cache {
constructor() {
this.map = new Map();
this.weakmap = new WeakMap();
}
setKey(key) {
const map = this[isObject(key) ? 'weakmap' : 'map'];
return map.set(key, new Cache());
}
getKey(key) {
const map = this[isObject(key) ? 'weakmap' : 'map'];
return map.get(key);
}
}
function getCache(args, cache) {
for (const key of args) {
cache = cache.getKey(key) || cache.setKey(key).get(key);
// cache = cache.getKey(key) || cache.setKey(key).getKey(key);
}
return cache;
}
function memoize(fn) {
const cache = new Cache();
return (...args) => {
const item = getCache(args, cache);
if (Reflect.has(item, 'value')) {
return item.value;
}
return (item.value = fn(args));
};
}
let counter = 1;
function foo() {
counter += 1;
return counter;
}
const id1 = Symbol('id');
const id2 = Symbol('id');
const memoizedFoo = memoize(foo);
console.log(memoizedFoo(id1)); // 2
console.log(memoizedFoo(id1)); // 2
console.log(memoizedFoo(id2)); // 3
console.log(memoizedFoo(id2)); // 3
Any help is greatly appreciated.
Unfortunately, if you're trying to "GET" a key, you cannot return the map, as you're asking for the key, so it must return the key. So you can't chain that method. But any method that shouldn't return something by default (IE would be void), we can do this:
cache = cache.getKey(key) || cache.setKey(key).get(key);
This will return a boolean, as the || symbol means return true if either is true.
But if you'd like to do the sort of chaining that JQuery does, that's easily achievable! What you want to do is return this or the object in every method.
Like so:
function isObject(arg) {
const typeOfObj = typeof arg;
return (typeOfObj === 'object' || typeOfObj === 'function') && arg !== null;
}
class Cache {
constructor() {
this.map = new Map();
this.weakmap = new WeakMap();
}
setKey(key) {
const map = this[isObject(key) ? 'weakmap' : 'map'];
map.set(key, new Cache());
return this; // HERE'S THE IMPORTANT PART
}
}
let counter = 1;
function foo() {
counter += 1;
return counter;
}
const id1 = Symbol('id');
const id2 = Symbol('id');
const memoizedFoo = memoize(foo);
console.log(memoizedFoo(id1)); // 2
console.log(memoizedFoo(id1)); // 2
console.log(memoizedFoo(id2)); // 3
console.log(memoizedFoo(id2)); // 3
This is my implementation of a graph, to get the shortest path between A and B.
class Queue {
constructor() {
this.head = null;
this.tail = null;
this.size = 0;
}
offer(item) {
const p = new QueueNode(item);
this.size++;
if (this.head === null) {
this.head = p;
this.tail = p;
return;
}
this.tail.next = p;
this.tail = p;
}
poll() {
if (this.size === 0) {
throw TypeError("Can't deque off an empty queue.");
}
this.size--;
const item = this.head;
this.head = this.head.next;
return item.val;
}
peek() {
if (this.size === 0) {
throw TypeError("Empty Queue.")
}
return this.head.val;
}
isEmpty() {
return this.head === null;
}
}
class QueueNode {
constructor(item) {
this.val = item;
this.next = null;
}
}
class Graph {
constructor(directed = false) {
this.numVertices = 0;
this.directed = directed;
this.dict = {}
}
addEdge(v1, v2, weight = 1) {
let p, q;
if (v1 in this.dict) {
p = this.dict[v1];
} else {
p = new GraphNode(v1);
this.dict[v1] = p;
this.numVertices++;
}
if (v2 in this.dict) {
q = this.dict[v2];
} else {
q = new GraphNode(v2);
this.dict[v2] = q;
this.numVertices++;
}
p.addEdge(q);
if (!this.directed) {
q.addEdge(p);
}
}
stringify() {
for (const [key, value] of Object.entries(this.dict)) {
console.log(`${key}: ${[...value.adjacencySet].map(x => x.data)}`);
}
}
buildDistanceTable(source) {
let p;
if (this.dict[source] === undefined) {
throw TypeError('Vertex not present in graph')
} else {
p = this.dict[source];
}
const distanceTable = {};
for (const [key, value] of Object.entries(this.dict)) {
distanceTable[key] = [-1, -1];
}
distanceTable[p.data] = [0, p.data];
const queue = new Queue();
queue.offer(p);
while (!queue.isEmpty()) {
let curr = queue.poll();
let curr_distance = distanceTable[curr.data][0];
curr.adjacencySet.forEach((item) => {
if (distanceTable[item.data] === -1) {
distanceTable[item.data] = [1 + curr_distance, curr.data];
console.log(distanceTable);
if (item.adjacencySet.length > 0) {
queue.offer(item);
}
}
})
}
return distanceTable;
}
shortestPath(source, destination) {
const distanceTable = this.buildDistanceTable(source);
const path = [destination];
let prev = distanceTable[destination][1];
while (prev !== -1 && prev !== source) {
path.unshift(prev);
prev = distanceTable[prev][1];
}
if (prev === null) {
console.log("There's no path from source to destination");
} else {
path.unshift(source);
path.map(item => {
console.log(item);
});
}
}
}
class GraphNode {
constructor(data) {
this.data = data;
this.adjacencySet = new Set();
}
addEdge(node) {
this.adjacencySet.add(node)
}
}
graph = new Graph(directed = false);
graph.addEdge(0, 1);
graph.addEdge(1, 2);
graph.addEdge(1, 3);
graph.addEdge(2, 3);
graph.addEdge(1, 4);
graph.addEdge(3, 5);
graph.addEdge(5, 4);
graph.addEdge(3, 6);
graph.addEdge(6, 7);
graph.addEdge(0, 7);
graph.stringify();
graph.shortestPath(1, 7);
When I run this it give 1, 7 however that's not the shortest path. What am I doing wrong here.
You have 2 issue in your code (that sabotage the building of the distance table):
You missing index in: if (distanceTable[item.data] === -1) { -> each item in the distance table is of array therefor it need to be: if (distanceTable[item.data][0] === -1) {
Set size in node js checked with size and not length (as in documentation) therefor item.adjacencySet.length is always undefined so you need to change: if (item.adjacencySet.length> 0) { to if (item.adjacencySet.size > 0) {
After those 2 changes your code return me path of 1 -> 0 -> 7
Just small side issue: you missing some ; and "new" before throwing TypeError...
What is the best way to have js return undefined rather than throw an error when a parent property does not exist?
Example
a = {}
b = a.x.y.z
// Error: Cannot read property 'value' of undefined
// Target result: b = undefined
You have to check for the existence of each property:
var b;
if (a.x && a.x.y && a.x.y.z) {
b = a.x.y.z
}
Or, simliar to another poster's "safeGet" function:
var get = function (obj, ns) {
var y = ns.split('.');
for(var i = 0; i < y.length; i += 1) {
if (obj[y[i]]) {
obj = obj[y[i]];
} else {
return;
}
}
return obj;
};
Use:
var b = get(a, 'x.y.z');
try {
a = {}
b = a.x.y.z
}
catch (e) {
b = void 0;
}
I would go for slightly verbose:
var b = ((a.x || {}).y || {}).z
you could write a safeGet helper function, something like:
edited for drilldown as suggested in comments by arcyqwerty
var getter = function (collection, key) {
if (collection.hasOwnProperty(key)) {
return collection[key];
} else {
return undefined;
}
};
var drillDown = function (keys, currentIndex, collection) {
var max = keys.length - 1;
var key = keys[currentIndex];
if (typeof collection === 'undefined') {
return undefined;
}
if (currentIndex === max) {
return getter(collection, key);
} else {
return drillDown(keys, currentIndex + 1,
getter(collection, key));
}
};
var safeGet = function (collection, key) {
if (key.indexOf(".") !== -1) {
return drillDown(key.split("."), 0, collection);
} else {
return getter(collection, key);
}
};
a = { x: 1 };
b = safeGet(a, 'x.y.z');
http://jsfiddle.net/YqdWH/2/