关联数组操纵挣扎

时间:2015-04-11 09:06:33

标签: javascript arrays

我试图理解以下代码中发生的事情:

var dataset = [
    {source: "Microsoft", target: "Amazon", type: "licensing"},
    {source: "Microsoft", target: "HTC", type: "licensing"},
    {source: "Samsung", target: "Apple", type: "suit"},
    {source: "Motorola", target: "Apple", type: "suit"},
    {source: "Nokia", target: "Apple", type: "resolved"},
    {source: "HTC", target: "Apple", type: "suit"},
    {source: "Kodak", target: "Apple", type: "suit"},
    {source: "Microsoft", target: "Barnes & Noble", type: "suit"},
    {source: "Microsoft", target: "Foxconn", type: "suit"},
    {source: "Oracle", target: "Google", type: "suit"},
    {source: "Apple", target: "HTC", type: "suit"},
    {source: "Microsoft", target: "Inventec", type: "suit"},
    {source: "Samsung", target: "Kodak", type: "resolved"},
    {source: "LG", target: "Kodak", type: "resolved"},
    {source: "RIM", target: "Kodak", type: "suit"},
    {source: "Sony", target: "LG", type: "suit"},
    {source: "Kodak", target: "LG", type: "resolved"},
    {source: "Apple", target: "Nokia", type: "resolved"},
    {source: "Qualcomm", target: "Nokia", type: "resolved"},
    {source: "Apple", target: "Motorola", type: "suit"},
    {source: "Microsoft", target: "Motorola", type: "suit"},
    {source: "Motorola", target: "Microsoft", type: "suit"},
    {source: "Huawei", target: "ZTE", type: "suit"},
    {source: "Ericsson", target: "ZTE", type: "suit"},
    {source: "Kodak", target: "Samsung", type: "resolved"},
    {source: "Apple", target: "Samsung", type: "suit"},
    {source: "Kodak", target: "RIM", type: "suit"},
    {source: "Nokia", target: "Qualcomm", type: "suit"}
];

var nodes = {};
update(dataset);

function update(links){
    var i = 1;
    links.forEach(function(link){
        // Pre iteration checks
        console.log(i);
        console.log(link.source);
        console.log(nodes[link.source]);
        // Observations – initial dataset is being overwritten? 

        // Should the following be read as “if one evaluates to true, do both?”
        link.source = nodes[link.source] || (nodes[link.source] = {name: link.source, linkCount:0}); // Initialize new nodes with zero links
        link.source.linkCount++;

        // Post iteration checks
        console.log(link.source);
        console.log(nodes[link.source]); // This is undefined if link.source is now an object. This is effectively saying console.log(nodes[object])
        console.log(nodes[link.source.name]); // This now says console.log(nodes["Microsoft"]) (for ex)
        i++;
    });
}

如果我写下我认为每次迭代中发生的事情,我会看到以下内容:

  • 迭代1:"Microsoft" = nodes["Microsoft"]?假。
  • 设置nodes["Microsoft"] = { name: "Microsoft", linkCount: 0 }
  • 增加基础节点linkCount + 1,以便nodes["Microsoft"] = { name: "Microsoft", linkCount: 1 }

  • 迭代2:"Microsoft" = nodes["Microsoft?"]?真。
  • 增加基础节点linkCount + 1,以便nodes["Microsoft"] = { name: "Microsoft", linkCount: 2 }

  • 迭代3:"Samsung" = nodes["Samsung"]?假。
  • 设置nodes["Samsung"] = { name: "Samsung", linkCount: 0 }
  • 增加基础节点linkCount + 1以及nodes["Samsung"] = { name: "Samsung", linkeCount: 1 }

我怀疑我的混淆与OR(||)运算符的工作原理有关。我的想法是,如果LHS评估为假,跳过它并进行RHS。这是错的吗?

编辑:

如果我只看第一次迭代;

dataset[0].source = nodes[dataset[0].source] || (nodes[dataset[0].source] = { name: dataset[0].source })

我们希望双方都能实现。 dataset [0] .source应设置为nodes [dataset [0] .source]并且我们要为节点[dataset [0] .source]设置'value'。

如果我试图像这样跑;

dataset[0].source = nodes[dataset[0].source];
nodes[dataset[0].source] = { name: dataset[0].source };

然后第一行运行正常,可能是交换“Microsoft”指向节点[“Microsoft”](当前未定义)的指针,但随后尝试用数据集[0]更新节点[“Microsoft”]的名称.source值,未定义,仅将nodes [“microsoft”]的名称值设置为undefined。

如果我尝试这样的代码;

dataset[0].source = nodes[dataset[0].source] || nodes[dataset[0].source] = { name: dataset[0].source };

然后我收到错误“Uncaught ReferenceError:赋值中的左侧无效” - 不确定为什么会这样?

最后,如果我尝试

dataset[0].source = nodes[dataset[0].source] || (nodes[dataset[0].source] = { name: dataset[0].source });

在RHS周围加上括号,一切正常。 dataset [0] .source是对象,节点[dataset [0] .source]也是对象。

为什么这三种不同的代码结构出现这种情况最终是我不明白的,具体为什么第三种代码工作

2 个答案:

答案 0 :(得分:0)

binary logical operator ||也称为“默认”运算符。它通常不返回布尔值,而是返回第一个值,除非它是假的,在这种情况下返回第二个值。换句话说,

a || b

字面上返回 a b

e.g。 null || 4返回4.

是的,如果左侧是真的,则右侧表达式被完全忽略(解析但未计算)。

对于link.source,通过尝试为该字符串分配属性,代码隐式字符串转换为对象。字符串和数字都可以用这种方式装箱,既可以使用继承的方法和属性(例如link.source.length来获取字符串的长度),也可以为它们分配新的方法和属性。他们通过这样做来保留他们的类型,不像你做new String(link.source)时发生的那样:它创造了一个在各方面都表现得像一个真实的对象。

答案 1 :(得分:0)

我不确定混淆在哪里,但代码正在以这种方式工作:

var dataset = [ /*data*/]
var nodes = {}

update(dataset);

正如预期的那样,将大量数据和节点初始化为空对象文字,然后调用更新函数。

-

function update(links) {
    var i = 1;
    links.forEach(function(link)
    {
        //content
        i++;
    });
}

这是迭代你的数组,增加i的值。

这里有两个略微相切的点 - 而不是自己递增,传递给foreach的回调将index作为其第二个参数;如果支持旧浏览器,请小心使用forEach,因为它不适用于IE8之前的浏览器。

link.source = nodes[link.source] || (nodes[link.source] = {name: link.source, linkCount:0});    // initialize new nodes with zero links
link.source.linkCount++;

这是你的循环代码的关键。懒惰的“OR”运算符与你提到的完全相同 - 如果左侧是真实的,那么它不会评估第一部分,所以

true || foo();

永远不会评估“foo”功能。因此,如果nodes [link.source]确实存在(并且是真实的,但定义的对象总是真实的),那么它会忽略第二部分并将该值分配给link.source,从而覆盖现有的数据集。

如果左侧的表达式为false,则它将评估您的右侧并在节点中创建节点并将其分配给link.source。

这意味着在运行代码的最后,节点将是一个包含linkCounts和名称属性的对象;和数据集将是相同的数组,但每个对象的source属性由相同的linkCount / name对象关联。

-

我不完全确定您希望代码如何工作,因此我不确定这与您预期的运行有何不同。

可能有问题的一件事是你讨论比较link.source和节点[link.source],但这不会发生在你编写的代码中,因为你使用的是单个等号(“=”)赋值运算符,而不是比较运算符的double或triple equals(“==”或“===”)?如果你想要,那么相关的行将是:

(link.source == nodes[link.source]) || (nodes[link.source] = {name: link.source, linkCount:0});

我会谨慎使用这种技巧方式的操作符,因为聪明的代码通常很难读懂,如果这就是你想要的,我会赞成标准的提高可读性的声明:

if(link.source !== nodes[link.source]) {
    nodes[link.source] = {name: link.source, linkCount:0}
}

但是,如果这就是你想要的,那么它会给你一个奇怪而毫无意义的输出... = \