gpt4 book ai didi

javascript - 高级 JavaScript : Partial cloning of an object graph

转载 作者:行者123 更新时间:2023-11-28 00:45:55 24 4
gpt4 key购买 nike

在客户端,我有一个复杂的 Javascript 对象可供使用。但当将其发送到服务器时,我只想发送其中的一部分。

我想克隆它,但只克隆我需要的部分。我想要一个可以实现类似功能的函数

var wireFriendly = reduce(original, "id, name, related.id, related.name");
// or
var wireFriendly = reduce(original, ["id", "name", "related.id", "related.name"]);

到目前为止我做了什么

我已经创建了这个函数的大部分内容,当对象图仅由对象组成时,它可以满足我的要求。如果我里面有数组,它就不起作用。但这是我到目前为止得到的:

function (entity, keepMembersList) {
// check entity that it's not undefined, null and that it's an actual Object instance
if (Object.prototype.toString.call(entity) !== "[object Object]")
{
throw "'entity' parameter should be a non-null object."
}

// code in switch statement only normalizes function arguments when
// they are provided either as a string or as an array of string
switch (Object.prototype.toString.call(keepMembersList))
{
case '[object String]':
keepMembersList = keepMembersList.split(',').map(function (value) {
return value.trim();
});
case '[object Array]':
keepMembersList = keepMembersList.map(function (value) {
return value.split(".");
});
break;
default:
throw "Parameter 'keepMembersList' should either be a comma delimited list of members' names to keep or an array of them.";
}

// from this point on, cloning is taking place
var result = {};

for (var i = 0; i < keepMembersList.length; i++)
{
for (var j = 0, r = result, e = entity; j < keepMembersList[i].length - 1; j++)
{
// ----------------------------------------------------------
// this part should be changed to also detect array notations
// ----------------------------------------------------------
r = r[keepMembersList[i][j]] = r[keepMembersList[i][j]] || {};
e = e[keepMembersList[i][j]];
}
r[keepMembersList[i][j]] = e[keepMembersList[i][j]];
}

return result;
}

我希望它也适用于这几个示例

// clone all array elements with specific members
var wireFriendly = reduce(original, "id, name, related[].id, related[].name");
// clone specific array element with specific members
var wireFriendly = reduce(original, ["id", "name", "related[0].id", "related[0].name"]);

但我至少可以接受第一个示例,其中数组的所有元素都被成员缩减。仅克隆特定数组元素的第二个示例对于我的情况来说并不重要,但我认为拥有它也很棒。当然,在这种情况下,结果数组上的索引不必匹配,否则我们会遇到未定义的元素:

//in case related[3].id would be specified we'd end up with
related:[undefined, undefined, undefined, { id: 1 }];

示例数据

使用该对象作为需要部分克隆的原始实体:

var original = {
"related":[{
"related":[],
"id":1759807323,
"name":"Related name",
"interest":{
"id":1314962015,
"name":"Ideas",
"isLocked":false,
"isPrivileged":false
},
"details":"Lengthy related details that will not be sent to server"
}],
"name":"My name",
"interest":{
"id":1314962015,
"name":"Ideas",
"isLocked":false,
"isPrivileged":false
},
"details":"Some lengthy details"
}

您可以使用 this JSFiddle 中的此函数测试工作示例.

最佳答案

解决方案

我提供自己的答案以供将来引用。最终的解决方案分为两步:

  1. 将多级对象层次结构扁平化为单个级别,同时将属性过滤到所需的集合
  2. 将单级对象还原为多级实例

请注意,这会创建原始对象的简化浅拷贝,因为我不会进一步操作它来意外更改原始对象。如果您需要克隆,您可以通过检测日期、正则表达式和其他实际需要创建新实例的内容,向展平取消展平函数添加附加功能。

测试对象(问题上方)在展平后看起来像这样:

var flattened = {
"name": "My name",
"details": "Some lengthy details",
"interest.id": 1314962015,
"interest.name": "Ideas",
"interest.isLocked": false,
"interest.isPrivileged": false,
"related[0].related": [],
"related[0].id": 1759807323,
"related[0].name": "Related name",
"related[0].interest.id": 1314962015,
"related[0].interest.name": "Ideas",
"related[0].interest.isLocked": false,
"related[0].interest.isPrivileged": false,
"related[0].details": "Lengthy related details that will not be sent to server"
};

属性被重命名以表示多级对象。

展平函数

我尝试了几种不同的版本,从使用堆栈迭代到递归。这个表现最好。

// "keep" is a HashMap-like object detailed later
function flatten(obj, keep) {

// normalize parameters
keep = keep || { contains: function () { return true; } };

var result = {};

var traverse = function (current, prefix) {
switch (Object.prototype.toString.call(current))
{
case "[object Object]":
for (var prop in current)
{
traverse(current[prop], (prefix.length ? prefix + "." : "") + prop);
}
// when there were no properties it's an empty object instance
if (!prop && keep.contains(prefix))
{
result[prefix] = {};
}
break;
case "[object Array]":
// arrays
for (var i = 0, l = current.length; i < l; i++)
{
traverse(current[i], (prefix.length ? prefix : "") + "[" + i + "]");
}
// when there were no elements it's an empty array instance'
if (l === 0 && keep.contains(prefix))
{
result[prefix] = [];
}
break;
case "[object Null]":
case "[object Undefined]":
case "[object Function]":
// don't use nulls, undefineds or functions
break;
default:
// primitive values: string, number, boolean, date, regexp
if (keep.contains(prefix))
{
result[prefix] = current;
}
}
};

traverse(obj, "");
return result;
};

取消展平函数

最快的反扁平化是使用 indexOfsubstring 函数而不是 split 并迭代生成的数组。

// "obj" is a flattened object instance
var unflatten = function unflatten(obj) {
var result = {},
current,
prop,
currIdx,
normalized;

for (var props in obj)
{
normalized = props.replace(/\[(\d+)\]/gi, ".$1");
current = result;
currIdx = -2;
while (currIdx !== -1)
{
prop = normalized.substring(
++currIdx,
(currIdx = normalized.indexOf(".", currIdx)) !== -1 ? currIdx : undefined
);
if (currIdx > 0)
{
current = current[prop] || (current[prop] = isNaN(parseInt(normalized.substring(currIdx + 1))) ? {} : []);
}
}
current[prop] = obj[props];
}

return result;
};

类 HashMap 类型

正如您在 flatten 函数中看到的,keep 参数的类型为 HashMap,具有 contains 函数。当您不提供任何 keep 参数时,flatten 将保留所有属性,因此默认 keep 实例位于函数开头。

var hashMapRx = /\[\d*\]/gi;

// Fast dictionary-like searching
var HashMap = function constructor(stringArray) {
this.hash = {};

for (var i = 0, l = stringArray.length; i < l; i++)
{
this.hash[this.normalize(stringArray[i])] = true;
}
};

HashMap.prototype.contains = function contains(value) {
var val = this.normalize(value);
return !!this.hash[val] || this.any(val);
};

HashMap.prototype.normalize = function normalize(value) {
return value.replace(hashMapRx, "[]");
};

HashMap.prototype.any = function any(value) {
var result = false;
for (var key in this.hash)
{
if (value === key || value.substr(0, key.length) === key)
return true;
}
return false;
};

实例化HashMap并提供以下形式的字符串数组时

["id", "name", "addresses[0].street", "addresses[].country" ...]

这些名称已标准化。我特意在这里提供了一个带有数组索引 (0) 的属性,而另一个则没有。它们都被标准化为没有索引的版本。因此,当发生展平时,所有数组项都会根据其属性定义包含在内。

减少变得像调用一样简单:

var reducedObj = unflatten(flatten(obj, keep));

关于javascript - 高级 JavaScript : Partial cloning of an object graph,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27401063/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com