JS:在一行代码中初始化并推送到 multi-dimensional 数组?创建树结构
JS: Initialize & push to multi-dimensional array in a single line of code? Create a tree structure
解决方案
感谢 Dave 的优雅解决方案,下面的答案是解决方案。旁注:首先,戴夫在下面提供的额外见解或作业对新手非常有价值。帮助我们伸展。
此代码将遍历现有的 JSON 树,例如您出于任何原因想要解析每个值。它不建造,而是行走。在我的例子中,我正在将每条评论解析为更丰富的 class:
var db = [], instance = {}, commentCounter = [];
function hydrateComments(items, parent) {
_.forEach(items, function(item) {
_.has(item, 'descendants') ? hydrateComments(item.descendants, item) : 0;
instance = new CommentModel(_.omit(item,['descendants']));
// other parsers go here, example the counter for each level
// parseCounter(comment);
(parent['children'] = (parent['children'] || [])) && item.depth > 0 ?
parent.children.push(instance) :
parent.push(instance);
});
}
hydrateComments(comments, storeComments);
ANGULAR 指令
对于那些使用此代码构建树的人,我包含了一个指令,可以帮助您使用上述树构建树。
请注意我已经删除了很多我自己的代码并且没有对此进行测试,但我知道我花了很多时间试图找到树和模板所以希望这对你有帮助。
buildTree.$inject = [];
function buildTree() {
link.$inject = ["scope", "elem", "attrs"];
function link(scope, elem, attrs) {
}
CommentController.$inject = ["$scope"];
function CommentController($scope) {
$scope.$watchCollection(function () {
return CommentDataService.getComments();
},
function (newComments, oldValue) {
if (newComments) {
$scope.comments.model = newComments;
}
}, true);
}
return {
restrict: "A",
scope: {
parent: "=cmoParent"
},
template: [
"<div>",
"<script type='text/ng-template'",
"id=" + '"' + "{[{ parent.app_id }]}" + '"' + " >",
"<div class='comment-post-column--content'>",
"<div cmo-comment-post",
"post-article=parent",
"post-comment=comment>",
"</div>",
"</div>",
"<ul ng-if='comment.children'>",
"<li class='comment-post-column--content'",
"ng-include=",
"'" + '"' + "{[{ parent.app_id }]}" + '"' + "'",
"ng-repeat='comment in comment.children",
"track by comment.app_id'>",
"</li>",
"</ul>",
"</script>",
"<ul class='conversation__timeline'>",
"<li class='conversation__post-container'",
"ng-include=",
"'" + '"' + "{[{ parent.app_id }]}" + '"' + "'",
"ng-repeat='comment in comments.model[parent.app_id]",
"track by comment.app_id'>",
"</li>",
"<ul>",
"</div>"
].join(' '),
controller: CommentController,
link: link
}
}
奖金
我还发现了一个绝招。如何使用一行代码初始化和填充数组。在我的例子中,我有一个计数器方法,可以计算我使用提示的每个级别的每个评论:
parseCounter: function(comment) {
var depth = comment.depth;
(commentCounter[depth] = (commentCounter[depth] || [])) ? commentCounter[depth]++ : 0;
},
原始问题
下面的代码解析 object 的 multi-level 数组,目的是将所有 object 解析为 “CommentModel”
的实例,虽然在这个例子中很简单object class 更丰富,但为了简洁起见,我简化了 object/class.
现有堆栈交换内容:
关于设置 multi-dimensional 数组的内容很多,几乎都显示了示例,例如:
var item[‘level1’][‘level2’] = ‘value’;
或
var item = [];
var item['level1'] = [];
或
var item = new Array([]) // and or objects
但是,没有这样的例子:
var item[‘level1’].push(object)
问题:
有没有办法在一行代码中初始化一个 2 级深度的 multi-dimensional 数组,同时推送给它?
1.1 即在我下面的 parent[‘children’]
示例中,我不得不检查它是否存在,如果没有设置它。如果我尝试 parent[‘children’].push(instance)
我显然会收到未定义异常的推送。是否有单行或更好的方法来检查 属性 是否存在?我显然不能在每次迭代时都在 parent 上设置一个空数组,即 parent[‘children’] = [];和 parent[‘children’] = 值不起作用
是否可以将初始化和验证移动到
CommentModel instance? 我试着问
CommentModel.prototype['children'] = []
;但随后所有 child ('descendants')
object 被添加到原型 属性 中的每个 object 中
“children”,有道理。
附带问题 - 我认为我的树迭代代码 function hydrateComments(items, parent)
很简洁,但是我可以做些什么来进一步简化 lodash and/or angular?我见过的大多数例子往往很冗长,并没有真正走到树枝上。
插件和代码
https://plnkr.co/edit/iXnezOplN4hNez14r5Tt?p=preview
var comments = [
{
id: 1,
depth: 0,
subject: 'Subject one'
},
{
id: 2,
depth: 0,
subject: 'Subject two',
descendants: [
{
id: 3,
depth: 1,
subject: 'Subject two dot one'
},
{
id: 4,
depth: 1,
subject: 'Subject two dot two'
}
]
},
{
id: 5,
depth: 0,
subject: 'Subject three',
descendants: [
{
id: 6,
depth: 1,
subject: 'Subject three dot one'
},
{
id: 7,
depth: 1,
subject: 'Subject three dot two',
descendants: [
{
id: 8,
depth: 2,
subject: 'Subject three dot two dot one'
},
{
id: 9,
depth: 2,
subject: 'Subject three dot two dot two'
}
]
}
]
}
];
function hydrateComments(items, parent) {
_.forEach(items, function (item) {
// create instance of CommentModel form comment. Simply example
var instance = new CommentModel(item);
// if we have descendants then injec the descendants array along with the
// current comment object as we will use the instance as the "relative parent"
if (_.has(instance, 'descendants')) {
hydrateComments(instance.descendants, instance);
}
// we check is parent has a property of children, if not, we set it
// NOTE : 3 lines of code ? is there a more concise approach
if (!_.has(parent, 'children')) {
parent['children'] = [];
}
// if depth id greater than 0, we push all instances of CommentModel of that depth to the
// parent object property 'children'. If depth is 0, we push to root of array
if (item.depth > 0) {
parent.children.push(instance);
} else {
parent.push(instance);
}
})
}
// simple example, but lets assume much richer class / object
function CommentModel(comment) {
this.id = comment.id;
this.depth = comment.depth;
this.subject = comment.subject;
this.descendants = comment.descendants;
}
var output = [];
// init - pass in data and the root array i.e. output
hydrateComments(comments, output);
// Tada - a hydrated multi-level array
console.log('Iteration output for comments : ', output)
要在单个语句中初始化数组,您可以按如下方式进行
Method 1: (To initialise parent['children']
) ANS to Q#1
#1 的傻瓜:https://plnkr.co/edit/lmkq8mUWaVrclUY2CoMt?p=preview
function hydrateComments(items, parent) {
_.forEach(items, function(item) {
// create instance of CommentModel form comment. Simply example
var instance = new CommentModel(item);
// if we have descendants then injec the descendants array along with the
// current comment object as we will use the instance as the "relative parent"
_.has(instance, 'descendants') ? hydrateComments(instance.descendants, instance) : 0;
//Less eff. and less readable then method #2
(parent['children'] = (parent['children'] || [])) && item.depth > 0 ?
parent.children.push(instance) :
parent.push(instance);
});
}
Method 2: (To initialise parent['children']
) ANS to Q#2 -- I'd prefer this.
#2 的傻瓜:https://plnkr.co/edit/zBsF5o9JMb6ETHKOv8eE?p=preview
function CommentModel(comment) {
this.id = comment.id;
this.depth = comment.depth;
this.subject = comment.subject;
this.descendants = comment.descendants;
//Initialise children in constructer itself! :)
this.children = [];
}
function hydrateComments(items, parent) {
_.forEach(items, function(item) {
// create instance of CommentModel form comment. Simply example
var instance = new CommentModel(item);
// if we have descendants then injec the descendants array along with the
// current comment object as we will use the instance as the "relative parent"
_.has(instance, 'descendants') ? hydrateComments(instance.descendants, instance) : 0;
item.depth > 0 ? parent.children.push(instance) : parent.push(instance);
});
}
ANS to Q#3
我觉得你的代码没问题。但是如果深度增加太多,你可能会遇到 Whosebug
。使用 trampolines 消除递归问题。但是如果你确定深度不是
我想引用上面文章中的几行:
What this graph doesn’t show is that after 30,000 recursive
invocations the browser hung; to the point it had to be forcibly shut
down. Meanwhile the trampoline continued bouncing through hundreds of
thousands of invocations. There are no practical limits to the number
of bounces a trampoline can make.
但只有用trampoline
才知道深度够深,会导致栈溢出
希望对您有所帮助!
解决方案
感谢 Dave 的优雅解决方案,下面的答案是解决方案。旁注:首先,戴夫在下面提供的额外见解或作业对新手非常有价值。帮助我们伸展。
此代码将遍历现有的 JSON 树,例如您出于任何原因想要解析每个值。它不建造,而是行走。在我的例子中,我正在将每条评论解析为更丰富的 class:
var db = [], instance = {}, commentCounter = [];
function hydrateComments(items, parent) {
_.forEach(items, function(item) {
_.has(item, 'descendants') ? hydrateComments(item.descendants, item) : 0;
instance = new CommentModel(_.omit(item,['descendants']));
// other parsers go here, example the counter for each level
// parseCounter(comment);
(parent['children'] = (parent['children'] || [])) && item.depth > 0 ?
parent.children.push(instance) :
parent.push(instance);
});
}
hydrateComments(comments, storeComments);
ANGULAR 指令
对于那些使用此代码构建树的人,我包含了一个指令,可以帮助您使用上述树构建树。
请注意我已经删除了很多我自己的代码并且没有对此进行测试,但我知道我花了很多时间试图找到树和模板所以希望这对你有帮助。
buildTree.$inject = [];
function buildTree() {
link.$inject = ["scope", "elem", "attrs"];
function link(scope, elem, attrs) {
}
CommentController.$inject = ["$scope"];
function CommentController($scope) {
$scope.$watchCollection(function () {
return CommentDataService.getComments();
},
function (newComments, oldValue) {
if (newComments) {
$scope.comments.model = newComments;
}
}, true);
}
return {
restrict: "A",
scope: {
parent: "=cmoParent"
},
template: [
"<div>",
"<script type='text/ng-template'",
"id=" + '"' + "{[{ parent.app_id }]}" + '"' + " >",
"<div class='comment-post-column--content'>",
"<div cmo-comment-post",
"post-article=parent",
"post-comment=comment>",
"</div>",
"</div>",
"<ul ng-if='comment.children'>",
"<li class='comment-post-column--content'",
"ng-include=",
"'" + '"' + "{[{ parent.app_id }]}" + '"' + "'",
"ng-repeat='comment in comment.children",
"track by comment.app_id'>",
"</li>",
"</ul>",
"</script>",
"<ul class='conversation__timeline'>",
"<li class='conversation__post-container'",
"ng-include=",
"'" + '"' + "{[{ parent.app_id }]}" + '"' + "'",
"ng-repeat='comment in comments.model[parent.app_id]",
"track by comment.app_id'>",
"</li>",
"<ul>",
"</div>"
].join(' '),
controller: CommentController,
link: link
}
}
奖金
我还发现了一个绝招。如何使用一行代码初始化和填充数组。在我的例子中,我有一个计数器方法,可以计算我使用提示的每个级别的每个评论:
parseCounter: function(comment) {
var depth = comment.depth;
(commentCounter[depth] = (commentCounter[depth] || [])) ? commentCounter[depth]++ : 0;
},
原始问题
下面的代码解析 object 的 multi-level 数组,目的是将所有 object 解析为 “CommentModel”
的实例,虽然在这个例子中很简单object class 更丰富,但为了简洁起见,我简化了 object/class.
现有堆栈交换内容:
关于设置 multi-dimensional 数组的内容很多,几乎都显示了示例,例如:
var item[‘level1’][‘level2’] = ‘value’;
或
var item = [];
var item['level1'] = [];
或
var item = new Array([]) // and or objects
但是,没有这样的例子:
var item[‘level1’].push(object)
问题:
有没有办法在一行代码中初始化一个 2 级深度的 multi-dimensional 数组,同时推送给它?
1.1 即在我下面的
parent[‘children’]
示例中,我不得不检查它是否存在,如果没有设置它。如果我尝试parent[‘children’].push(instance)
我显然会收到未定义异常的推送。是否有单行或更好的方法来检查 属性 是否存在?我显然不能在每次迭代时都在 parent 上设置一个空数组,即 parent[‘children’] = [];和 parent[‘children’] = 值不起作用是否可以将初始化和验证移动到 CommentModel instance? 我试着问
CommentModel.prototype['children'] = []
;但随后所有 child ('descendants') object 被添加到原型 属性 中的每个 object 中 “children”,有道理。附带问题 - 我认为我的树迭代代码
function hydrateComments(items, parent)
很简洁,但是我可以做些什么来进一步简化 lodash and/or angular?我见过的大多数例子往往很冗长,并没有真正走到树枝上。
插件和代码
https://plnkr.co/edit/iXnezOplN4hNez14r5Tt?p=preview
var comments = [
{
id: 1,
depth: 0,
subject: 'Subject one'
},
{
id: 2,
depth: 0,
subject: 'Subject two',
descendants: [
{
id: 3,
depth: 1,
subject: 'Subject two dot one'
},
{
id: 4,
depth: 1,
subject: 'Subject two dot two'
}
]
},
{
id: 5,
depth: 0,
subject: 'Subject three',
descendants: [
{
id: 6,
depth: 1,
subject: 'Subject three dot one'
},
{
id: 7,
depth: 1,
subject: 'Subject three dot two',
descendants: [
{
id: 8,
depth: 2,
subject: 'Subject three dot two dot one'
},
{
id: 9,
depth: 2,
subject: 'Subject three dot two dot two'
}
]
}
]
}
];
function hydrateComments(items, parent) {
_.forEach(items, function (item) {
// create instance of CommentModel form comment. Simply example
var instance = new CommentModel(item);
// if we have descendants then injec the descendants array along with the
// current comment object as we will use the instance as the "relative parent"
if (_.has(instance, 'descendants')) {
hydrateComments(instance.descendants, instance);
}
// we check is parent has a property of children, if not, we set it
// NOTE : 3 lines of code ? is there a more concise approach
if (!_.has(parent, 'children')) {
parent['children'] = [];
}
// if depth id greater than 0, we push all instances of CommentModel of that depth to the
// parent object property 'children'. If depth is 0, we push to root of array
if (item.depth > 0) {
parent.children.push(instance);
} else {
parent.push(instance);
}
})
}
// simple example, but lets assume much richer class / object
function CommentModel(comment) {
this.id = comment.id;
this.depth = comment.depth;
this.subject = comment.subject;
this.descendants = comment.descendants;
}
var output = [];
// init - pass in data and the root array i.e. output
hydrateComments(comments, output);
// Tada - a hydrated multi-level array
console.log('Iteration output for comments : ', output)
要在单个语句中初始化数组,您可以按如下方式进行
Method 1: (To initialise
parent['children']
) ANS to Q#1
#1 的傻瓜:https://plnkr.co/edit/lmkq8mUWaVrclUY2CoMt?p=preview
function hydrateComments(items, parent) {
_.forEach(items, function(item) {
// create instance of CommentModel form comment. Simply example
var instance = new CommentModel(item);
// if we have descendants then injec the descendants array along with the
// current comment object as we will use the instance as the "relative parent"
_.has(instance, 'descendants') ? hydrateComments(instance.descendants, instance) : 0;
//Less eff. and less readable then method #2
(parent['children'] = (parent['children'] || [])) && item.depth > 0 ?
parent.children.push(instance) :
parent.push(instance);
});
}
Method 2: (To initialise
parent['children']
) ANS to Q#2 -- I'd prefer this.
#2 的傻瓜:https://plnkr.co/edit/zBsF5o9JMb6ETHKOv8eE?p=preview
function CommentModel(comment) {
this.id = comment.id;
this.depth = comment.depth;
this.subject = comment.subject;
this.descendants = comment.descendants;
//Initialise children in constructer itself! :)
this.children = [];
}
function hydrateComments(items, parent) {
_.forEach(items, function(item) {
// create instance of CommentModel form comment. Simply example
var instance = new CommentModel(item);
// if we have descendants then injec the descendants array along with the
// current comment object as we will use the instance as the "relative parent"
_.has(instance, 'descendants') ? hydrateComments(instance.descendants, instance) : 0;
item.depth > 0 ? parent.children.push(instance) : parent.push(instance);
});
}
ANS to Q#3
我觉得你的代码没问题。但是如果深度增加太多,你可能会遇到 Whosebug
。使用 trampolines 消除递归问题。但是如果你确定深度不是
我想引用上面文章中的几行:
What this graph doesn’t show is that after 30,000 recursive invocations the browser hung; to the point it had to be forcibly shut down. Meanwhile the trampoline continued bouncing through hundreds of thousands of invocations. There are no practical limits to the number of bounces a trampoline can make.
但只有用trampoline
才知道深度够深,会导致栈溢出
希望对您有所帮助!