【问题标题】:Selecting last element in JavaScript array [duplicate]选择JavaScript数组中的最后一个元素[重复]
【发布时间】:2012-02-21 10:46:07
【问题描述】:

我正在制作一个实时更新用户位置和路径并将其显示在 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'])是。
  • 以相反的顺序存储您的位置并调用 loc[0] 怎么样?
  • @Christophe 我也想过这个问题,但如果他跟踪它们一段时间,他将轻松获得超过 1,000 个位置,性能可能会成为问题。追加到末尾比将它们向下移动要快得多。
  • @LeviMorrison:如果可以选择更改数组,那么您可以简单地使用unshift(),这应该比手动“移动”元素快得多。要在数组中只有 1000 个元素,并将它们添加到开头,只需使用以下命令(其中arr 是存储要存储的元素的数组):arr.unshift(new_element); arr.splice(1000);unshift() 在数组的开头添加元素,splice(1000) 删除前 1000 个元素之后的所有内容(如果数组更短,则不会删除任何内容)。它符合您的需求吗?
  • 使用.length-1 将是the fastest 它看起来像。

标签: javascript arrays google-maps google-maps-markers


【解决方案1】:

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

看起来是这样的:

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/

【讨论】:

  • 老实说,如果这是他的应用程序的关键部分,他应该使用对象,而不仅仅是原型原语。见my post
  • @LeviMorrison:您的回答更多是关于组织脚本/项目,而不是解决这个特定问题。向原型添加(自定义)函数可能不是那么干净,但老实说:arr[arr.length - 1] 是访问arr 数组的最后一个元素的常见且易于理解的方式。
  • 这是一个旧答案,但尤其是现在请避免改变任何内置原型(例如,Array.prototype.last = 是一个不安全的分配)。
  • 我不是第一个这么说的人,也不会是最后一个:不要修改你不拥有的对象——尤其是原生原型。当 JavaScript 在 ES 规范中实现相同的方法名称但与您的略有不同时会发生什么?坏事。 Prototype 和 Sugar.js 都修改了 native。最后,他们花费的时间和金钱都比他们节省的要多。
  • 我一直想知道为什么 JavaScript 不能将它从 this.locations[this.locations.length-1] 减少到 this.locations[-1]。这不是让我们的生活更轻松吗?有什么理由不这样做?
【解决方案2】:

使用slice() 方法:

my_array.slice(-1)[0]

【讨论】:

  • 这是一个很好的答案!它可以在单行中使用(my_array 可以替换为返回数组的表达式或函数),并且您不必对 Array 原型进行全局更改。
  • 也是extremely slow
  • 确实这个很慢,可以参考jsperf.com/last-element-of-array-xxx
  • 相对慢,但不是绝对慢。比替代方案慢约 100 倍,但较慢的版本仍然在我的浏览器中每秒执行近 1000 万次。
  • 我添加了一个test case,您可以在其中连续调用.pop().push()。似乎比 .slice(-1)[0] 快 10 倍,但仍然比直接访问慢 10 倍。
【解决方案3】:

您也可以.pop 关闭最后一个元素。 小心,这会改变数组的值,但这对你来说可能没问题。

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

【讨论】:

  • @JosefJezek:是的,慢一点。但是,我的意思是,你仍然可以在一秒钟内完成 2 亿次这样的操作,所以在这种情况下,最好担心什么是最可读的,而不是担心性能。 (当然,除非您尝试每秒执行数亿次)
  • 只要使用arr.concat().pop()
【解决方案4】:

使用 es6 解构数组和扩展运算符

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

注意 yourArray 不会改变。

【讨论】:

  • 这不会创建yourArray 的副本,弹出最后一个元素然后扔掉临时副本吗?如果yourArray 有大量元素,我认为这将是一项代价高昂的操作。
  • @chitti 或者他们只是转到最后一个索引并为您提供该元素的副本
【解决方案5】:
var arr = [1, 2, 3];
arr.slice(-1).pop(); // return 3 and arr = [1, 2, 3]

如果数组为空,这将返回 undefined 并且不会改变数组的值。

【讨论】:

  • 我喜欢这个。它不会像arr[arr.length - 1]; 那样快,因为您正在实例化一个新数组,但至少它不是一些缓慢和/或破坏性的方法arr.reverse()[0][...arr].pop();,而且您不会改变内置的-在原型中(非常糟糕的做法),就像大多数其他解决方案一样。我要补充一点,现在你可以使用解构赋值并取消pop(),这在某些情况下可能是有意义的。例如const arr = [1,2,3]; const [last] = arr.slice(-1);
  • 很好,结果很完美。
【解决方案6】:
var last = array.slice(-1)[0];

我发现 -1 处的 slice 对于获取最后一个元素(尤其是未知长度的数组)很有用,而且性能比计算长度小于 1 的要好得多。

Mozilla Docs on Slice

Performance of the various methods for selecting last array element

【讨论】:

  • +1 为jsperf.com/get-last-item-from-array JavaScript 性能链接,但是,对于我尝试过的浏览器和计算机array[array.length - 1] 至少执行array.slice(-1)[0] 40:1。
  • 刚刚点击了我自己的链接并意识到我误读了图表:-P
【解决方案7】:

UnderscoreLodash 具有 _.last(Array) 方法,它返回数组中的最后一个元素。它们的工作原理相同

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

Ramda 也有一个_.last 函数

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

【讨论】:

    【解决方案8】:

    这行得通:

    array.reverse()[0]
    

    【讨论】:

    • 请解释你的代码在做什么。
    • 欢迎来到 Stack Overflow!虽然这段代码 sn-p 可以解决问题,但including an explanation 确实有助于提高帖子的质量。请记住,您正在为将来的读者回答问题,而这些人可能不知道您的代码建议的原因。也请尽量不要用解释性 cmets 挤满你的代码,因为这会降低代码和解释的可读性!
    • 其实很不错的想法。 Javascript reverse()做你所期望的,最后一项成为你可以通过[0]解决的第一项。它是所有浏览器都可用的本机功能,因此速度在这里应该不是问题。 警告:developer.mozilla.org/en/docs/Web/JavaScript/Reference/… 说它正在改变数组。所以进一步访问反向数组可能不是你所期望的!
    • 除了改变数组,这也会非常慢。
    • @svarog 这只会增加答案的时间复杂性。不必要的慢。
    【解决方案9】:

    如果你使用的是 ES6,你可以这样做:

    const arr = [ 1, 2, 3 ];
    [ ...arr ].pop(); // 3
    arr; // [ 1, 2, 3 ] (wasn't changed)
    

    【讨论】:

    • 这和Array.prototype.slice(arr).pop();一样,纯粹是为了得到最后一个元素而复制整个数组。
    • 看起来很短,但与其他方法相比肯定慢
    【解决方案10】:

    所以,很多人都在回答 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
    

    一种流行音乐,就像 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] => 未定义(因为如果您这样做,您正在寻找第 4 个值(此深度级别适用于任何不幸的新手在这里结束))。

    对于您的应用程序来说,可能不是最好的,甚至不是一个好的方法,比如流量、流失等等。但是对于遍历一个数组,将其流式传输到另一个数组,只是愚蠢地使用低效的方法,this。完全是这样。

    【讨论】:

    • “所以,很多人都用 pop() 来回答,但他们中的大多数人似乎没有意识到这是一种破坏性的方法。”实际上,他们都意识到这是一种破坏性的方法。您引用的答案以粗体清楚地说明了这一点,而涉及 pop 的所有其他答案都会首先复制数组。
    【解决方案11】:

    您可以在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

    如您所见,访问看起来不像函数调用; getter 函数在内部调用。

    【讨论】:

    • 修改不属于你的对象的原型是不好的做法。
    【解决方案12】:

    如果这对您的应用程序至关重要,请使用 JavaScript 对象。您不应该使用原始原语来管理应用程序的关键部分。由于这似乎是您的应用程序的核心,您应该改用对象。我在下面编写了一些代码来帮助您入门。 lastLocation 方法将返回最后一个位置。


    function User(id) {
        this.id = id;
    
        this.locations = [];
    
        this.getId = function() {
            return this.id;
        };
    
        this.addLocation = function(latitude, longitude) {
            this.locations[this.locations.length] = new google.maps.LatLng(latitude, longitude);
        };
    
        this.lastLocation = function() {
            return this.locations[this.locations.length - 1];
        };
    
        this.removeLastLocation = function() {
            return this.locations.pop();
        };
    
    }
    
    function Users() {
        this.users = {};
    
        this.generateId = function() {
            return Math.random();
        };
    
        this.createUser = function() {
            var id = this.generateId();
            this.users[id] = new User(id);
            return this.users[id];
        };
    
        this.getUser = function(id) {
            return this.users[id];
        };
    
        this.removeUser = function(id) {
            var user = this.getUser(id);
            delete this.users[id];
    
            return user;
        };
    
    }
    
    
    var users = new Users();
    
    var user = users.createUser();
    
    user.addLocation(0, 0);
    user.addLocation(0, 1);
    

    【讨论】:

    • 我正在尝试使用它来重组我的程序。我是 JS 新手,但这比我目前拥有的要干净得多。你能解释一下吗?就像我要从数据库中提取坐标和用户 ID,我将如何触发程序?我正在使用 AJAX 继续调用从我的数据库中提取坐标的程序。
    • 原语适用于简单的操作。并非所有地方都需要 OO。
    • 这是一种正确的方法。对象倾向于使代码清晰易用。但是这里出了点问题:你没有使用原型来定义你的函数。这对性能不利,在定义函数时始终使用原型属性。每次创建此对象时,都会“即时”添加其他功能。
    • “您不应该使用原始原语来管理应用程序的关键部分。”这里的理由是什么?
    • @LeviMorrison 好吧,我想这是一个观点。虽然我不同意。我参加的编程语言课程强调了函数式编程语言倾向于对原语、集合/列表和字典进行操作。这工作得很好,并且考虑到 JS 更像是一种功能语言而不是 OO 语言,我认为没有必要仅仅为了满足某人对编程的普遍看法而引入“对象”
    【解决方案13】:
    var last = function( obj, key ) { 
        var a = obj[key];
        return a[a.length - 1];
    };
    
    last(loc, 'f096012e-2497-485d-8adb-7ec0b9352c52');
    

    【讨论】:

    • @LeviMorrison:在这种情况下,您也可以在Array 的原型中添加一个方法。
    • @LeviMorrison:请参阅我的答案,了解向所有数组添加新方法的方式,因此获取最后一个元素更容易。
    • 拥有像这个答案这样的顶级 fn 比向原型添加方法要好,因为它不会掩盖通过封装进行的访问。
    猜你喜欢
    • 1970-01-01
    • 2016-04-20
    • 1970-01-01
    • 1970-01-01
    • 2019-10-12
    • 2020-08-30
    • 2017-11-28
    • 1970-01-01
    • 2023-04-09
    相关资源
    最近更新 更多