如何在JavaScript中更新/添加数组元素?

13

如何更新/添加数组中的元素?

var persons = {
    data: []
};

var bob = {name: 'Bob', age: 15};
var fill = {name: 'Fill', age: 20};
var mark = {name: 'Mark', age: 19};
var john = {name: 'John', age: 4};

persons['data'].push(bob);
persons['data'].push(fill);
persons['data'].push(mark);
persons['data'].push(john);

var updatedJohn = {name: 'John', age: 100};

if (!persons['data'][updatedJohn.name]){
    persons['data'].push(updatedJohn);
} else {
    persons['data'][updatedJohn.name] = updatedJohn; // this line doesn't work
}

如果数组中已经存在元素John,我无法弄清如何更新该元素。

更新

jsFiddle示例


如果两个人有相同的名字会发生什么? - axelduch
在这种情况下,我们需要更新一个已经存在的人(John)。这个数组就像一个“集合”。 - Alex
6个回答

12
你需要一个查询函数,如下所示,以帮助你根据数据库中的属性查找索引:(JSFiddle)
function findIndexByProperty(data, key, value) {
    for (var i = 0; i < data.length; i++) {
        if (data[i][key] == value) {
            return i;
        }
    }
    return -1;
}

var johnIndex = findIndexByProperty(persons.data, 'name', 'John');
if (johnIndex > -1) {
    persons.data[johnIndex] = updatedJohn;
} else {
    persons.data.push(updatedJohn);
}

请注意,这仅返回名称为John的第一条记录。在多个这样的记录的情况下,您需要决定希望它执行什么操作 - 更新所有记录吗?这种问题是数据库通常具有唯一键以标识记录的原因。
如果使用Underscore或lodash,则已经有一个_.findIndex()函数可供使用:
var johnIndex = _.findIndex(persons.data, { name: 'John' }); 

2021年更新:使用现代JavaScript,您可以用以下代码替换该函数:

const johnIndex = persons.data.findIndex(p => p.name === "John")

3
为什么不使用关联数组来代替persons['data'],因为.push()只能使用索引数组。
var persons = {
   data: {}
};

...
persons['data'][john.name] = john;
...

var updatedJohn = {name: 'John', age: 100};

// if statement isn't neccesary no more bc
// if the index is there it will override it and if not
// it will create a new index
persons['data'][updatedJohn.name] = updatedJohn;

这行代码没有起作用是因为人员数组使用了整数索引persons['data'].push(),而不是字符串索引 persons['data'][stringIndex]


1
@papirtiger,我不知道是谁教你的,但关联数组(对象)在任何语言中都非常有用,而不仅仅是在js中。为什么我会期望我使用关联键创建的数组是数字的呢? - Jay Harris
在JavaScript中使用对象非常有用,但是在数组上使用字符串索引违反了最小惊奇原则。问题在于,如果我使用您的代码,我会期望array[0]是第一个成员,array[array.length - 1]是最后一个成员。 - max
1
@papirtiger 那篇文章是在2006年写的。我可以理解为什么会有那种观点,但是像PHP一样,JS也给开发者更多的自由度。我为你这位朋友更好地解释了我的答案。 - Jay Harris
@papirtiger,答案假设.data是一个空对象{}。当然,我错过了问题中的[]。无论如何,我的回答都是正确的lol。 - Jay Harris
道格拉斯·克罗克福德和很多人意见不同。;-) - jonhobbs
显示剩余2条评论

1
在Javascript中,数组是由数字索引的(或者至少应该是)。
persons['data'].push(bob);
persons['data'].push(fill);
persons['data'].push(mark);

使用persons[2]将给您{name: 'Mark', age: 19}。

Javascript非常灵活,可以像@Sasa的回答一样使用字符串索引,但如果这样做,其他开发人员可能会削弱你,因为这是一种非常糟糕的做法。

[添加] 考虑以下这些奇怪和意外的行为示例:

var ary = [];
ary[0] = false;
ary['using string indexes in javascript arrays is stupid'] = true;
console.log('pop should return the last added element', ary.pop()); //  false - WTF?
console.log('lastIndexOf should be 1?', ary.lastIndexOf(true)); // -1 WTF?

Javascript中的数组应该用作栈。想象一下一叠纸牌,你可以添加(push)或取走(pull)牌,但是你不知道哪张牌在哪里(除非你作弊)。

如果您想按名称列出人员,则应使用对象存储人员:

persons.data = {};
persons['data']['Bob'] = bob;

或者,您可以筛选数组以获取与谓词匹配的值:

bob = persons.data.filter(function(person){
  return person["name"] === 'Bob';
})[0];

编辑:

示例:创建或查找一个人的函数。

var persons = { data : [] }
persons.data.push({ name: 'Bob', age: 10 })

// returns the index of the newly added person
persons.addOrReplace = function(new_person, attr) {

    var attr = attr || 'name';
    var data = this.data;    
    for (var i = 0; i < data.length; i++) {
      if (data[i][attr] === new_person[attr]) {
        data[i] = new_person;
        return i;
      }
    }   
    return this.data.push(new_person);
}

persons.addOrReplace({ name: 'Bob', age: 11 });
console.log(persons.data.length); // 1
console.log(persons.data[0].age); // 11

persons.addOrReplace({ name: 'Joe', age: 11 });
console.log(persons.data.length); // 2
console.log(persons.data[persons.data.length -1].name); // Joe

这种做法非常不好的原因是,由于您拥有完全适合字典(键值对,也称为哈希或关联数组)的对象,我们期望数组按插入顺序进行数字索引。 - max
如果您使用filter方法,然后尝试使用bob = updatedBob更改记录,则persons.data内部的记录不会被更新。 - Stuart
@Stuart。你错了,这个对象是按引用传递的。http://jsfiddle.net/4xp02cuu/ - max
你可以更改它的个别属性,比如 bob.name = 'Bobby',但是你不能使用 bob = updatedBob 替换整个记录,这就是 OP 试图做的事情。http://jsfiddle.net/4xp02cuu/1/ - Stuart
添加了一个查找或替换函数的示例。 - max
@Stuart 当然你是正确的,如果要替换一个元素,你需要通过索引访问数组。 - max

1
尝试遍历persons对象的元素,如果存在相同名称的成员,则更新该元素,如果不存在,则将一个新元素推送到数组中。使用一个新变量exists来检查成员是否存在。
以下是您可以执行的操作:
var persons = {
    data: []
};

var bob = {name: 'Bob', age: 15};
var fill = {name: 'Fill', age: 20};
var mark = {name: 'Mark', age: 19};
var john = {name: 'John', age: 4};

persons['data'].push(bob);
persons['data'].push(fill);
persons['data'].push(mark);
persons['data'].push(john);

var updatedJohn = {name: 'John', age: 100};

var exists = 0;

for (var i=0; i<persons['data'].length; i++) {
    if (persons['data'][i].name == updatedJohn.name) {
        persons['data'][i].age = updatedJohn.age;
        exists = 1;
    }
}

if (exists === 0)
    persons['data'].push(updatedJohn);

这是你更新后的代码片段: http://jsfiddle.net/t4kjgkcn/3/

@Stuart,你能澄清一下吗?我在这里测试过了,对我有效:http://jsfiddle.net/95d9oLkq/1/ - benomatis
尝试使用 console.log(persons.data) 进行调试 - 对于每个没有名称为“John”的现有记录,您已将“updatedJohn”的副本添加到数据库中。 - Stuart

1

如果每个现有的人不在查找表中,您可以为其创建一个查找表,将其推入persons.data并在查找表中创建一个新条目;否则,您需要完全重写person对象,而不会破坏给定的值引用。(我写了一条注释来解释这部分内容)。

这里是JSFiddle

var persons = {
    data: []
};

var bob = {name: 'Bob', age: 15};
var fill = {name: 'Fill', age: 20};
var mark = {name: 'Mark', age: 19};
var john = {name: 'John', age: 4};

persons['data'].push(bob);
persons['data'].push(fill);
persons['data'].push(mark);
persons['data'].push(john);

var personsHash = {};

// store an external reference for each persons
for (var i = 0, l = persons.data.length; i < l; i++) {
    personsHash[persons.data[i].name] = persons.data[i];
}

var updatedJohn = {name: 'John', age: 100};

if (!personsHash[updatedJohn.name]){
    personsHash[updatedJohn.name] = updatedJohn;
    persons['data'].push(updatedJohn);
} else {
    var key;
    var person = personsHash[updatedJohn.name];
    // comment if you don't want a full rewrite but just an update of attributes
    /**/
    for (key in person) {
        delete person[key];
    }
    /**/
    for (key in updatedJohn) {
        if (updatedJohn.hasOwnProperty(key)) {
            person[key] = updatedJohn[key];
        }
    }
}

如果旧的John已经存在,我就不需要添加一个全新的John。在这种情况下,我只需要用新的John替换旧的John。就这样。但是你的解决方案确实添加了新的John对象。 - Alex
是的,它只会在不存在时才添加。我只是使用了一种完全重写旧John的方法,但从不打破对象的引用(不使用=)。 - axelduch
哦,是的,我的错,我没有针对persons.data,而是针对了persons,让我来修复一下。 - axelduch

0

function addOrUpdate(arr, comp, update) {
  if (arr) {
    var updated = false;
    arr.map(function(e, i) {
      var found = e[comp] == update[comp];
      if (found) {
        angular.extend(arr[i], update);
        updated = true;
      }
    });
    if (!updated) {
      arr.push(update);
    }
  }
}

//example

var a = [{
  id: 1,
  name: 'a'
}, {
  id: 2,
  name: 'b'
}, {
  id: 3,
  name: 'c'
}];

addOrUpdate(a, "id", {
  id: 4,
  name: 'e3333'
});
//looks for an elemnent with id=4, doesnt find, and adds it

addOrUpdate(a, "id", {
  id: 4,
  name: 'e5555'
});
//this time it will find and update the name


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接