{"id":11240,"date":"2020-06-06T07:24:21","date_gmt":"2020-06-06T07:24:21","guid":{"rendered":"http:\/\/www.softwareeverydayblog.com\/?p=11240"},"modified":"2020-06-06T07:27:00","modified_gmt":"2020-06-06T07:27:00","slug":"programming-problem-time-based-key-value-store","status":"publish","type":"post","link":"https:\/\/www.softwareeverydayblog.com\/?p=11240","title":{"rendered":"[Programming Problem] Time Based Key-Value Store"},"content":{"rendered":"<p>Create a timebased key-value store class TimeMap, that supports two operations.<\/p>\n<ol>\n<li>set(string key, string value, int timestamp)<br \/>\nStores the key and value, along with the given timestamp.<\/li>\n<li>get(string key, int timestamp)<br \/>\nReturns a value such that set(key, value, timestamp_prev) was called previously, with timestamp_prev <= timestamp.\nIf there are multiple such values, it returns the one with the largest timestamp_prev.\nIf there are no values, it returns the empty string (\"\").<\/li>\n<\/ol>\n<p>[<a href=\"https:\/\/leetcode.com\/problems\/time-based-key-value-store\/\" rel=\"noopener noreferrer\" target=\"_blank\">Problem Link<\/a>]<\/p>\n<p>Note that key-values are inserted in timestamp order (meaning when we set multiple values for a key, we wont request timestamp 10 before timestamp 1-9). So, the timestamp&#8217;s for a key will be in sorted order.<\/p>\n<p>The problem essentially boils down to <strong>running a binary search for a target (or next smallest target)<\/strong>. At every step, we return value if it matches exactly, otherwise go left or right. When we get a return from the recursion, we have a choice between returning a.) returned value or b.) current (mid) value. We return the one lower than target. If both values are lower than target, we return the one closer to target.<\/p>\n<pre lang=\"javascript\">\r\n\/**\r\n * Initialize your data structure here.\r\n *\/\r\n\r\nvar closest = function(i, j, arr, val) {\r\n    if ( i > j ) return {value: '', timestamp: Number.MAX_VALUE};\r\n    \r\n    let mid = i + Math.round((j-i)\/2);\r\n    if ( arr[mid].timestamp === val ) return arr[mid];\r\n    \r\n    let ret = Number.MAX_VALUE;\r\n    if ( arr[mid].timestamp > val )\r\n        ret = closest(i, mid-1, arr, val);\r\n    else \r\n        ret = closest(mid+1, j, arr, val);\r\n    \r\n    if ( arr[mid].timestamp < val &#038;&#038; ret.timestamp < val ) {\r\n        return arr[mid].timestamp > val ? arr[mid] : ret;\r\n    } else if ( arr[mid].timestamp < val ) {\r\n        return arr[mid];\r\n    } else {\r\n        return ret;\r\n    }\r\n}\r\n\r\n\r\nvar TimeMap = function() {\r\n    this.map = {};\r\n};\r\n\r\n\/** \r\n * @param {string} key \r\n * @param {string} value \r\n * @param {number} timestamp\r\n * @return {void}\r\n *\/\r\nTimeMap.prototype.set = function(key, value, timestamp) {\r\n    if (!this.map[key]) this.map[key] = [];\r\n    this.map[key].push({value, timestamp});\r\n};\r\n\r\n\/** \r\n * @param {string} key \r\n * @param {number} timestamp\r\n * @return {string}\r\n *\/\r\nTimeMap.prototype.get = function(key, timestamp) {\r\n    if (!this.map[key]) return \"\";\r\n    let arr = this.map[key];\r\n    let ret = closest(0, arr.length-1, arr, timestamp);\r\n    return ret.value;\r\n};\r\n\r\n\/** \r\n * Your TimeMap object will be instantiated and called as such:\r\n * var obj = new TimeMap()\r\n * obj.set(key,value,timestamp)\r\n * var param_2 = obj.get(key,timestamp)\r\n *\/\r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Create a timebased key-value store class TimeMap, that supports two operations. set(string key, string value, int timestamp) Stores the key and value, along with the given timestamp. get(string key, int timestamp) Returns a value such that set(key, value, timestamp_prev) was called previously, with timestamp_prev j ) return {value: &#8221;, timestamp: Number.MAX_VALUE}; let mid = i [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-11240","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/www.softwareeverydayblog.com\/index.php?rest_route=\/wp\/v2\/posts\/11240","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.softwareeverydayblog.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.softwareeverydayblog.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.softwareeverydayblog.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.softwareeverydayblog.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=11240"}],"version-history":[{"count":4,"href":"https:\/\/www.softwareeverydayblog.com\/index.php?rest_route=\/wp\/v2\/posts\/11240\/revisions"}],"predecessor-version":[{"id":11242,"href":"https:\/\/www.softwareeverydayblog.com\/index.php?rest_route=\/wp\/v2\/posts\/11240\/revisions\/11242"}],"wp:attachment":[{"href":"https:\/\/www.softwareeverydayblog.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=11240"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.softwareeverydayblog.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=11240"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.softwareeverydayblog.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=11240"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}