Array.sort doesn't work properly when called twice?

那年仲夏 提交于 2021-01-29 07:25:32

问题


I've been playing this for a while. Why is val_a changed when I run the function to sort val b? How can I get around this? When I run the function separately they work, but when I run them together something goes wrong somewhere.

var GLOBALS = {"items":[]};
var val_a;
var val_b;
GLOBALS.items=[
{"thing":"one","val_a":0.5,"val_b":0.2},
{"thing":"two","val_a":0.2,"val_b":0.3},
{"thing":"three","val_a":0.3,"val_b":0.1}];


	val_a = GLOBALS.items.sort(function (a, b) {
		a=parseFloat(a.val_a);
		b=parseFloat(b.val_a);

		if (a < b) {
			return -1;
		}
		if (a > b) {
			return 1;
		}
		return 0;
	});

	val_b = GLOBALS.items.sort(function (a, b) {
		a=parseFloat(a.val_b);
		b=parseFloat(b.val_b);

		if (a < b) {
			return -1;
		}
		if (a > b) {
			return 1;
		}
		return 0;
	});


	console.log("val_a",val_a);
	console.log("val_b",val_b);

回答1:


The reason you're seeing what you're seeing is that val_a and val_b just contain references to the same array, which is also referenced from GLOBALS.items. sort changes the state of the array that all three of those variables are pointing to.

Variables contain values, not objects. When dealing with objects (including arrays), the value in the variable is a reference to the object, which actually exists elsewhere in memory. Think of that reference as a number telling the JavaScript engine where the object is elsewhere in memory. (More below.)

If you want three separate arrays (the original items, then a copy sorted by val_a, and another sorted by val_b), you want to make a shallow copy of the array before sorting it, which you can do with slice:

val_a = GLOBALS.items.slice().sort(...);

About the array being elsewhere in memory, here's a simpler example:

var a = [42]; // A variable with a reference to an array, which is
              // elsewhere in memory

That gives us this in memory:

                    +---------+
a<REF5512>--------->| (array) |
                    +---------+
                    | 0: 42   |
                    +---------+

(That REF5512 is just completely made up to make the point that a reference is a value. We never see the raw reference values.)

Then if we do:

var b = a;    // A copy of that same reference

That gives us this in memory:

a<REF5512>---+
             |      +---------+
             +----->| (array) |
             |      +---------+
b<REF5512>---+      | 0: 42   |
                    +---------+

If we change the state of the array (for instance, by sorting it), naturally that change is visible from either variable, as they're both referring to the same array:

b[0] = 67;

gives us

a<REF5512>---+
             |      +---------+
             +----->| (array) |
             |      +---------+
b<REF5512>---+      | 0: 67   | State of the array changed
                    +---------+

slice creates a new array with a copy of the values in the old array (just the values; the objects in your array aren't copied). So for instance:

b = a.slice();

Gives us:

                    +---------+
a<REF5512>--------->| (array) |
                    +---------+
                    | 0: 67   |
                    +---------+

                    +---------+
b<REF7341>--------->| (array) |
                    +---------+
                    | 0: 67   |
                    +---------+

Note how the reference in b is no longer the same as in a.


Side note: Your sort callbacks can be much simpler:

val_a = GLOBALS.items.sort(function (a, b) {
    return parseFloat(a.val_a) - parseFloat(b.val_a);
});

The sort callback just has to return a value less than zero (it doesn't have to be specifically -1), greater than zero (it doesn't have to be specifically 1), or equal to zero.



来源:https://stackoverflow.com/questions/38159138/array-sort-doesnt-work-properly-when-called-twice

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!