如何对对象进行版本控制?

2024-01-06

为了解释这一点,请查看下面正在更改的对象:

obj = {'a': 1, 'b': 2} // Version 1
obj['a'] = 2 // Version 2
obj['c'] = 3 // Version 3

我希望能够获得该对象的任何这些版本,例如得到obj从版本 2 开始。我不想每次更新单个密钥时都存储整个对象的副本。

我怎样才能实现这个功能?

我尝试执行此操作的实际对象大约有 500,000 个键。这就是为什么我不想在每次更新时存储它的完整副本。我首选的理论解决方案编码语言是python or javascript,但我会接受任何东西。


您可以使用 ES6 代理来实现这一点。这些将捕获对象上的任何读/写操作,并将每个更改记录在更改日志中,该更改日志可用于前后滚动更改。

下面是一个基本实现,如果您打算在对象上应用除基本更新操作之外的其他功能,则可能需要更多功能。它允许获取当前版本号并将对象向后(或向前)移动到特定版本。每当您对对象进行更改时,它都会首先移至其最新版本。

此代码片段显示了一些操作,例如更改字符串属性、添加到数组以及移动它,同时前后​​移动到其他版本。

Edit:现在,它还能够将更改日志作为对象获取,并将该更改日志应用于初始对象。通过这种方式,您可以保存初始对象和更改日志的 JSON,并重放更改以获取最终对象。

function VersionControlled(obj, changeLog = []) {
    var targets = [], version = 0, savedLength, 
        hash = new Map([[obj, []]]),
        handler = {
            get: function(target, property) {
                var x = target[property];
                if (Object(x) !== x) return x;
                hash.set(x, hash.get(target).concat(property));
                return new Proxy(x, handler);
            },
            set: update,
            deleteProperty: update
        };

    function gotoVersion(newVersion) {
        newVersion = Math.max(0, Math.min(changeLog.length, newVersion));
        var chg, target, path, property,
            val = newVersion > version ? 'newValue' : 'oldValue';
        while (version !== newVersion) {
            if (version > newVersion) version--;
            chg = changeLog[version];
            path = chg.path.slice();
            property = path.pop();
            target = targets[version] || 
                     (targets[version] = path.reduce ( (o, p) => o[p], obj ));
            if (chg.hasOwnProperty(val)) {
                target[property] = chg[val];
            } else {
                delete target[property];
            }
            if (version < newVersion) version++;
        }
        return true;
    }
    
    function gotoLastVersion() {
        return gotoVersion(changeLog.length);
    }
    
    function update(target, property, value) {
        gotoLastVersion(); // only last version can be modified
        var change = {path: hash.get(target).concat([property])};
        if (arguments.length > 2) change.newValue = value;
        // Some care concerning the length property of arrays:
        if (Array.isArray(target) && +property >= target.length) {
            savedLength = target.length;
        }
        if (property in target) {
            if (property === 'length' && savedLength !== undefined) {
                change.oldValue = savedLength;
                savedLength = undefined;
            } else {
                change.oldValue = target[property];
            }
        }
        changeLog.push(change);
        targets.push(target);
        return gotoLastVersion();
    }
    
    this.data = new Proxy(obj, handler);
    this.getVersion = _ => version;
    this.gotoVersion = gotoVersion;
    this.gotoLastVersion = gotoLastVersion;
    this.getChangeLog = _ => changeLog;
    // apply change log
    gotoLastVersion();
}

// sample data
var obj = { list: [1, { p: 'hello' }, 3] };

// Get versioning object for it
var vc = new VersionControlled(obj);
obj = vc.data; // we don't need the original anymore, this one looks the same

// Demo of actions:
console.log(`v${vc.getVersion()} ${JSON.stringify(obj)}. Change text:`);
obj.list[1].p = 'bye';
console.log(`v${vc.getVersion()} ${JSON.stringify(obj)}. Bookmark & add property:`);
var bookmark = vc.getVersion();
obj.list[1].q = ['added'];
console.log(`v${vc.getVersion()} ${JSON.stringify(obj)}. Push on list, then shift:`);
obj.list.push(4); // changes both length and index '4' property => 2 version increments
obj.list.shift(); // several changes and a deletion
console.log(`v${vc.getVersion()} ${JSON.stringify(obj)}. Go to bookmark:`);
vc.gotoVersion(bookmark);

console.log(`v${vc.getVersion()} ${JSON.stringify(obj)}. Go to last version:`);
vc.gotoLastVersion();
console.log(`v${vc.getVersion()} ${JSON.stringify(obj)}. Get change log:`);
var changeLog = vc.getChangeLog();
for (var chg of changeLog) {
    console.log(JSON.stringify(chg));
}

console.log('Restart from scratch, and apply the change log:');
obj = { list: [1, { p: 'hello' }, 3] };
vc = new VersionControlled(obj, changeLog);
obj = vc.data;
console.log(`v${vc.getVersion()} ${JSON.stringify(obj)}`);
.as-console-wrapper { max-height: 100% !important; top: 0; }
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何对对象进行版本控制? 的相关文章

随机推荐

  • 捕获比屏幕大的整个滚动视图[重复]

    这个问题在这里已经有答案了 主要问题是将整个滚动视图保存为位图图像 而不仅仅是屏幕上显示的图像 有没有办法保存整个滚动视图 如果可以的话如何保存 在ScrollView中创建RelativeLayout或LinearLayout以从Layo
  • 使用 gstreamer 将 YUVj420p 像素格式转换为 RGB888

    我使用 gstreamer 1 2 将帧从我的 IP 摄像头馈送到 opencv 程序 流是 640 368 YUVj420p 我想将其转换为RBG888以便能够在我的opencv程序中使用它 那么有没有办法使用 gstreamer 来进行
  • Bash 4.2 中的 IFS 发生变化

    运行这些命令会给出预期的结果 bash version GNU bash version 4 1 11 2 release foo 111 222 333 IFS cat lt lt lt foo 111 222 333 然而 在 Bash
  • 如何从谷歌地图 URL 获取某个位置的地址?

    给定谷歌地图位置的 URL 我希望能够获取该位置的地址 例如给定这个 URL https www google com maps place Eiffel Tower 48 8583701 2 2922926 17z data 3m1 4b
  • 即使 upload_max_size 大于文件大小,$_FILE 上传大文件也会出现错误 1

    我有一个简单的上传表单 enctype multipart form data gt and input type hidden name MAX FILE SIZE value 5900000 gt 以及在 php ini 中应用 通过
  • 将参数传递给 link_to 方法

    如何使用 link to 方法通过 MVC 传递参数 view 如何使用 link to 方法来利用 remove tag 操作 issues controller rb def remove tag parameter issue rem
  • 黑莓无线安装

    我将黑莓应用程序的可交付变量上传到服务器 我希望我的用户从 URL 安装该应用程序 在上传到远程服务器之前 我在本地主机上进行了测试 没问题 但是 当我尝试从服务器下载 jad 文件时 它会显示文件内容 但不会安装应用程序 显示文字 Man
  • AngularJS 与 Django - 模板标签冲突

    我想将 AngularJS 与 Django 一起使用 但是它们都使用 作为他们的模板标签 有没有一种简单的方法可以更改两者之一以使用其他自定义模板标签 对于 Angular 1 0 您应该使用 interpolateProvider ap
  • 高优先级的自定义命令 Windows 服务

    我有一个部署在 Windows Server 2008 中的工作跟踪器 WPF 应用程序 该跟踪器应用程序正在通过 WCF 服务与 Tracker windows 服务进行通信 用户可以从 Worker Tracker GUI 应用程序创建
  • 如何使 css a:active 在点击后起作用?

    我正在尝试使菜单用作选项卡 选项卡本身工作正常 菜单链接也很棒 但我想删除活动选项卡的底部边框 使其看起来像在实际页面上 我尝试过使用 id a active但似乎只有当我按下链接时它才起作用 我也想过用 javascript 来做这件事
  • 在Python中获取Decimal的Ceil()?

    有没有办法在Python中获得高精度Decimal的ceil gt gt gt import decimal gt gt gt decimal Decimal 800000000000000000001 100000000000000000
  • 通过 NDK 在 Android 中使用 C 库

    我想做的事 我找到了一个计算音频流音高的 C 库 http www schmittmachine com dywapitchtrack html并想在 Android 中使用它 我想除了移植它之外 我还可以在 NDK 的帮助下使用它 对吧
  • 如何从正则表达式创建随机字符串

    我想从正则表达式生成一个随机字符串 example random string 0 9 4 gt 7895 random string 0 9 4 gt 0804 random string 0 9 A Z 4 gt 9ZE5 random
  • 如何在lucene中使用tf idf相似度对文档进行排名

    在创建索引和搜索查询的基本代码中 我想使用 TFIDFsimilarity 对检索到的文档进行排名 但我收到错误 无法实例化类型 TFIDFSimilarity 我的代码如下 public class TFIDF T private sta
  • 在 WooCommerce 中对特定类别的最便宜商品进行折扣

    我喜欢根据产品类别打折 Woocommerce 中最便宜的购物车商品 基于 Woocommerce 中成本较低的产品的购物车折扣 https stackoverflow com questions 49693564 cart discoun
  • org.w3c.dom.DOMException:HIERARCHY_REQUEST_ERR

    我对此已经关注太久了 无法弄清楚我做错了什么 因此 我正在尝试为某些内容生成 Xades 签名 不幸的是我总是遇到同样的错误 HIERARCHY REQUEST ERR 这是我的 XML 文档
  • 聚合时间戳数据的更好方法?

    我正在处理非统一收集的时间戳索引数据 最终将按每分钟 每小时计算统计数据 我想知道按时间段聚合的最佳方法是什么 我目前计算两个 lambda 函数 然后将两列添加到数据框中 如下所示 h lambda i pd to datetime i
  • 为什么我的桌面视图很慢?

    我正在制作一个加载了一些 NSArray 的表格视图 该单元格包含两个标签和一个加载了 URL 图像的背景图像视图 问题是 tableview 的滚动很慢 就像冻结或其他什么 我认为这是因为 Imageview 但我能做什么 这是我的一些代
  • 如何使用 Maven 程序集插件 jar-with-dependencies 在 JAR 中包含自定义文件

    我需要在最终的 JAR 中包含自定义文件 com app log4 properties 使用 jar with dependencies 时如何将一个文件添加到我的 JAR 中 现在该 JAR 中只有类文件 我在用着 mvn assemb
  • 如何对对象进行版本控制?

    为了解释这一点 请查看下面正在更改的对象 obj a 1 b 2 Version 1 obj a 2 Version 2 obj c 3 Version 3 我希望能够获得该对象的任何这些版本 例如得到obj从版本 2 开始 我不想每次更新