JavaScript数组中选择最后一个元素

775

我正在制作一个应用程序,实时更新用户的位置和路径,并在Google地图上显示。我有一个功能,可以使用对象同时跟踪多个用户,该对象每秒更新一次。

现在,当用户在Android应用中按下按钮时,坐标会被发送到数据库,并且每次位置发生变化时,地图上的标记也会更新(并形成一条折线)。

由于我有多个用户,因此我发送了一个唯一且随机生成的字母数字字符串,以便为每个用户显示单独的路径。当JS从数据库中提取这些数据时,它会检查用户是否存在,如果不存在,则创建一个带有值为列表的新键。它看起来像这样:

loc = {f096012e-2497-485d-8adb-7ec0b9352c52: [new google.maps.LatLng(39, -86),
                                              new google.maps.LatLng(38, -87),
                                              new google.maps.LatLng(37, -88)],
       44ed0662-1a9e-4c0e-9920-106258dcc3e7: [new google.maps.LatLng(40, -83),
                                              new google.maps.LatLng(41, -82),
                                              new google.maps.LatLng(42, -81)]}
我正在做的是将坐标列表作为键的值存储,该键是用户的ID。每次位置更改时,我的程序都会通过添加到列表来更新此列表(这正常工作)。
我需要做的是每次位置更改时更新标记的位置。我想通过选择数组中的最后一个项目来实现这一点,因为那将是最后已知的位置。目前,每次位置更改时都会向地图添加一个新标记(示例中的每个点都将在该位置显示一个标记),因此会继续添加标记。
我将使用 `for (x in loc)` 语句每次位置更新时从列表中获取最后一个位置,并使用它来更新标记。如何从哈希内的数组中选择此最后一个元素?

@tomdemuyt:loc不是一个数组,OP正在询问的是loc['f096012e-2497-485d-8adb-7ec0b9352c52'](或者loc['f096012e-2497-485d-8adb-7ec0b9352c52'])。 - zizozu
2
将您的位置以相反的顺序存储,然后调用 loc[0] 怎么样? - Christophe
1
@Christophe 我也考虑过这个问题,但如果他一直追踪它们,很容易就会得到超过1,000个位置,性能可能会成为一个问题。在末尾添加比将它们向下移动要快得多。 - Levi Morrison
3
如果更改数组是一个选项,那么你可以简单地使用 unshift(),这比手动“移动”元素要快得多。为了仅在数组中保留1000个元素,并在开头添加它们,只需使用以下代码(其中arr是存储要存储的元素的数组):arr.unshift(new_element); arr.splice(1000);unshift()在数组开始处添加元素,而splice(1000)删除第1,000个元素后的所有内容(如果数组较短,则不删除任何元素)。是否符合您的需求? - Tadeck
1
使用.length-1似乎是最快的方法。 - Qix - MONICA WAS MISTREATED
13个回答

1102

如何访问数组的最后一个元素

看起来像这样:

var my_array = /* some array here */;
var last_element = my_array[my_array.length - 1];

在您的情况下,它看起来像这样:

var array1 = loc['f096012e-2497-485d-8adb-7ec0b9352c52'];
var last_element = array1[array1.length - 1];

或者,更长的版本是不创建新变量:

loc['f096012e-2497-485d-8adb-7ec0b9352c52'][loc['f096012e-2497-485d-8adb-7ec0b9352c52'].length - 1];

如何添加一个更简单的获取方法

如果您喜欢创建函数或快捷方式来完成这样的任务,则可使用以下代码:

if (!Array.prototype.last){
    Array.prototype.last = function(){
        return this[this.length - 1];
    };
};

您可以通过调用数组的last()方法来获取数组的最后一个元素,在您的情况下,如下所示:

loc['f096012e-2497-485d-8adb-7ec0b9352c52'].last();

你可以在这里检查它是否有效:http://jsfiddle.net/D4NRN/


5
说实话,如果这是他的申请的重要部分,而且看起来确实是,他应该使用对象,而不仅仅是原型化的原始数据类型。请参见我的帖子 - Levi Morrison
29
@LeviMorrison:你的回答更多是关于组织脚本/项目,而不是解决这个具体问题。向原型添加(自定义)函数可能不太干净,但说实话:arr[arr.length - 1] 是访问 arr 数组的最后一个元素的常见且易于理解的方式。 - Tadeck
7
这是一个老问题,但尤其现在请避免突变任何内置原型(例如 Array.prototype.last = 是一种不安全的赋值)。 - Josh from Qaribou
17
我不是第一个说这话的人,也不会是最后一个:不要修改你不拥有的对象,尤其不要修改原生的原型。当JavaScript在实现与你的方法名略有不同的ES规范时会发生什么?糟糕的事情。Prototype和Sugar.js都修改了原生对象,在最终,它们比节省的时间和金钱给我和我的同事带来了更多的麻烦。 - Adam
11
我一直想知道为什么 JavaScript 不能将 this.locations[this.locations.length-1] 简化为 this.locations[-1]。这样不是会让我们的生活变得更轻松一些吗?有任何不这样做的理由吗? - newman
显示剩余14条评论

707

使用slice()方法:

my_array.slice(-1)[0]

13
这是一个很棒的答案!它可以用于一行代码中(my_array 可以被替换为返回数组的表达式或函数),而且你不需要对 Array 原型进行全局更改。 - Jim Pivarski
244
它也非常缓慢。链接 - Qix - MONICA WAS MISTREATED
8
确实,这非常慢,你可以参考 http://jsperf.com/last-element-of-array-xxx。 - Sean Larkin
83
它相对较慢,但并非完全缓慢。比起另一种选择,它慢了大约100倍,但在我的浏览器中,这个速度仍然达到了近1000万次每秒。 - chris
7
我添加了一个test case,其中你依次调用.pop().push()。这种方法似乎比.slice(-1)[0]快10倍,但仍然比直接访问慢10倍。 - mjois
显示剩余8条评论

185

您还可以使用.pop方法弹出数组中的最后一个元素。请注意,这将更改数组的值,但对您来说可能没有问题。

var a = [1,2,3];
a.pop(); // 3
a // [1,2]

3
慢慢地,查看这个测试 http://jsperf.com/last-element-of-array-xxx/2 - Josef Ježek
62
是的,速度会慢一些。但是,我的意思是,你仍然可以在一秒钟内完成2亿次这样的操作,所以最好关注可读性,不要担心性能问题,除非你当然想每秒钟做数亿次这样的操作。 - Colin DeClue
2
只需使用 arr.concat().pop() - ife

84

使用ES6的解构数组和展开运算符

var last = [...yourArray].pop();

注意,yourArray不会改变。


61
这样做会创建一个 yourArray 的副本,弹出最后一个元素,然后丢弃这个临时副本。如果 yourArray 元素数量很多,这将是一项代价高昂的操作。 - chitti
@chitti 或者它们只是转到最后一个索引并给您该元素的副本。 - magamig

54
var arr = [1, 2, 3];
arr.slice(-1).pop(); // return 3 and arr = [1, 2, 3]

如果数组为空,则此函数将返回undefined,并且此函数不会更改数组的值。


7
我喜欢这个方法。虽然它实例化了一个新数组,所以不会像arr[arr.length - 1];那么快,但至少它不是一些缓慢和/或有破坏性的方法,如 arr.reverse()[0][...arr].pop();。此外,你没有改变内置原型(非常糟糕的做法),这与大多数其他解决方案不同。我要补充的是,现在可以使用解构赋值来替代pop(),这在某些情况下可能更合适。例如:const arr = [1,2,3]; const [last] = arr.slice(-1); - Josh from Qaribou
太棒了,结果完美无缺。 - Marcial Cahuaya

38

UnderscoreLodash都有_.last(Array)方法,用于返回数组中的最后一个元素。它们的工作方式基本相同。

_.last([5, 4, 3, 2, 1]);
=> 1

Ramda也有一个_.last函数。

R.last(['fi', 'fo', 'fum']); //=> 'fum'

38

14
感谢分享 http://jsperf.com/get-last-item-from-array 这个 JavaScript 性能链接,但是在我尝试过的浏览器和电脑上,array[array.length - 1] 的性能要比 array.slice(-1)[0] 快至少40倍。 - Stephen Quan
7
刚刚点开自己的链接后,我意识到自己错读了图表 :-P - amay0048

19

这个有效:

array.reverse()[0]

7
请解释一下你的代码在做什么。 - Marco Scabbiolo
8
这实际上是一个相当不错的想法。Javascript中的 reverse() 函数会按预期将最后一项变为第一项,您可以通过 [0] 访问它。它是一个本地函数,在所有浏览器中都可用,因此速度不应该是问题。但是需要注意的是:https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/reverse 上说这个函数会更改数组本身。因此,进一步访问反转后的数组可能不是您所期望的! - neongrau
26
除了改变数组外,这样做也会非常缓慢。 - Dan Dascalescu
5
那只会增加答案的时间复杂度,没有必要这么慢。 - Mox
1
感谢您的贡献。问题要求“选择”最后一个元素。 “选择”意味着在过程中未修改数组。 .reverse()方法将改变(即修改)数组,这就是为什么我对您的答案进行了投票否决的原因。 - robert
显示剩余5条评论

17

很多人回答使用pop(),但他们中的大部分似乎没有意识到那是一种破坏性方法。

var a = [1,2,3]
a.pop()
//3
//a is now [1,2]

因此,针对一种非常愚蠢、无损的方法:

var a = [1,2,3]
a[a.push(a.pop())-1]
//3

一个 push pop,就像在90年代一样 :)

push 将一个值添加到数组的末尾,并返回结果的长度。因此

d=[]
d.push('life') 
//=> 1
d 
//=>['life']

pop返回数组中最后一个元素的值,在移除该索引处的值之前。因此

c = [1,2,1]
c.pop() 
//=> 1
c 
//=> [1,2]

数组从0开始索引,因此c.length => 3,c[c.length] => undefined(因为如果这样做,您将查找第4个值(深度级别适用于任何不幸的新手最终到达此处))。

可能不是您的应用程序的最佳方法,甚至不是好的方法,会有流量,流失等问题。但是对于遍历数组,将其流式传输到另一个数组,或者只是以低效的方法进行嬉闹,使用这个方法。完全可以这样做。


因此,很多人都回答使用pop()方法,但他们中的大多数似乎没有意识到这是一个破坏性的方法。实际上,所有人都意识到它是一种破坏性方法。你引用的答案清楚地用粗体标注了这一点,而其他每个使用pop的答案都会首先复制数组。 - MarredCheese

17

您可以在Array.prototype上定义一个getter

if (!Array.prototype.hasOwnProperty("last")) {
  Object.defineProperty(Array.prototype, "last", {
    get() {
      return this[this.length - 1];
    }
  });
}

console.log([9, 8, 7, 6].last); // => 6

正如您所看到的,访问看起来不像函数调用;获取器函数在内部被调用。


7
修改不属于您所有的对象的原型是一种不良做法。 - user1464581

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