Як поміняти місцями дві змінні в JavaScript


Відповіді:


321

Ось однолінійка для заміни значень двох змінних.
Дані змінні aта b:

b = [a, a = b][0];

Демонстрація нижче:

var a=1,
    b=2,
    output=document.getElementById('output');

output.innerHTML="<p>Original: "+a+", "+b+"</p>";

b = [a, a = b][0];

output.innerHTML+="<p>Swapped: "+a+", "+b+"</p>";
<div id="output"></div>


264
+1. Але сама коротка версія буде в ECMAScript 6: [a, b] = [b, a];.
dfsq

8
@Kay: Схоже, це набагато повільніше, використовуючи масив замість третьої змінної: http://jsperf.com/swap-array-vs-variable Я проте протестував це лише у Chrome. Я не зміг перевірити версію ECMAScript 6, оскільки вона наразі дає Invalid left-hand side in assignment помилку.
Неп

2
@ FrançoisWahl Добре. Я думаю, що більшість відповідей тут спрацює і є досить рівнозначними. Я припускаю, що це торгівля між тимчасовим змінним використанням, кількістю коду та швидкістю.
showdev

3
@ FrançoisWahl добре, я б не здогадався, що це рішення настільки повільніше. Дивіться також: jsperf.com/swap-array-vs-variable/3
kay - SE - зло

3
@showdev Прочитайте цитату Дейкстра у відповіді Теда Хоппа.
Брайан Маккутчон

185

ES6 (Firefox та Chrome вже підтримують його (Destructuring Assignment Array Matching)):

let a = 5, b = 6;
[a, b] = [b, a];
console.log(`${a} ${b}`);


2
хтось знає назву такого роду swap в es6?
дерек

6
@derek - Я думаю, що це називається збіг масиву , форма присвоєння руйнування .
Тед Хопп

4
Здається, це приблизно в 35 разів повільніше, ніж метод третьої змінної у nodejs 7.4.0 / win7 64. Але це, звичайно, акуратно.
mkey


5
Оскільки V8 версії 6.8, заміна змінних з деструктуризацією масиву повинна бути такою ж швидкою, як і з тимчасовою змінною ( v8project.blogspot.com/2018/06/v8-release-68.html ).
Рубен Верборг

120

Ви можете зробити це:

var a = 1,
    b = 2,
    tmp;
tmp = a;
a = b;
b = tmp;

Для читабельності та ремонтопридатності це неможливо перемогти (принаймні, у JavaScript). Усі, хто підтримує код (включаючи вас через півроку), точно знатимуть, що відбувається.

Оскільки це цілі числа, ви також можете використовувати будь-яку кількість розумних хитрощів 1 для обміну, не використовуючи третю змінну. Наприклад, ви можете використовувати оператор бітового xor:

let a = 1, b = 2;
a = a ^ b;
b = a ^ b;
a = a ^ b;
    
console.log('a is now:', a);
console.log('b is now:', b);

Це називається алгоритмом swap XOR. Його теорія функціонування описана в цій статті Вікіпедії .

1 "Грамотний програміст повністю усвідомлює обмежений розмір власного черепа. Тому він підходить до своєї задачі з повним смиренням і уникає розумних хитрощів, як чума". - Едсгер В. Дійкстра


Xor буде працювати з будь-яким типом даних. Це фокус віднімання, який працюватиме лише з числами.
Роб Грант

11
@RobertGrant - Оператор xor в JavaScript перетворює свої операнди в 32-бітні цілі числа (використовуючи ToInt32внутрішній метод - див. Розділ 11.10 стандарту ECMAScript ). Це не дає правильних результатів для не цілих числових значень. Він також перетворює нечислові значення в 32-бітні цілі числа. Якщо ви починаєте з a = "hi"і b = "there", ви закінчуєте з a == 0і b == 0.
Тед Хопп

1
Цей трюк працює з будь-яким типом даних, якщо ви не заперечуєте проти цілого результату; значення автоматично передаються в int32s. Це означає, що він може працювати з числовими рядками, булевими (0/1), null (0) та порожніми масивами / об'єктами (0). Хоча вихідний тип не зберігається, так подіяло Booleans не працюватиме з typeof a == 'boolean'або a === false, наприклад. Реальні числа працюють, за винятком того, що вони спрямовані на нуль проти округленого до найближчого цілого числа.
Beejor

1
@Beejor - Іншими словами, він працює з будь-яким типом даних, за винятком випадків, якщо це не (якщо вони не є цілими значеннями). У моїй книзі "swap" означає "закінчення кожної змінної, яка має значення, яку мала інша", а не "конвертувати в int32, а потім підміняти".
Тед Хопп

@TedHopp Ярмарок досить. Я мав на увазі "працює" з точки зору того, що ви можете кинути на нього будь-який тип даних, а не як це добре підходить для рішення. Я згоден, це взагалі не дуже корисно.
Bejjor

58

Не використовуйте наведений нижче код. Це не рекомендований спосіб замінювати значення двох змінних (просто використовуйте для цього тимчасову змінну ). Він просто показує хитрість JavaScript.

Це рішення не використовує жодних тимчасових змінних, без масивів, лише одне додавання, і це швидко . Насправді це іноді швидше, ніж тимчасова змінна на кількох платформах .
Він працює для всіх чисел, ніколи не переповнює і обробляє крайові випадки, такі як Infinity та NaN.

a = b + (b=a, 0)

Він працює в два етапи:

  • (b=a, 0)встановлює bстаре значення aі врожайність0
  • a = b + 0встановлює aстаре значенняb

5
Версія для тимчасової вар є дещо швидшою, а також загальнішою і читабельнішою. jsperf.com/swap-two-numbers-without-tmp-var/9
Сурма

Що означає цей () синтаксис? як це дає 0?
Піт Елвін

8
Оператор у круглих дужках є оператором комами ,, і він був завернутий для встановлення права пріоритету. Оператор комами оцінює обидва свої аргументи (у даному випадку b=aта 0) та повертає останній (у цьому випадку 0). Тож тут, це впливає на встановлення нового bна старе значення a, при цьому поступаючись 0.
Рубен Верборг

6
Я маю рацію, думаючи, що це працює лише для числового значення? Ви не можете використовувати це, наприклад, var a = "hello" b = "world" як a = "world0".
Chris GW Green

3
@ChrisGWGreen:a = b + (b=a, "")

19

З ES6 ви також можете міняти змінні більш елегантно:

var a = 1,
    b = 2;

[a, b] = [b, a];

console.log('a:', a, 'b:', b); // a: 2 b: 1

18

Ось один вкладиш, припускаючи , aі bвже існує і має значення , що вимагає , щоб поміняти місцями:

var c=a, a=b, b=c;

Як згадував @Kay, це насправді працює краще, ніж спосіб масиву (майже в 2 рази швидше).


1
Як і моя ідеальна відповідь, просто я вважаю за краще не переосмислювати змінну a & b під час заміни, а використовувати явну назву змінної "tmp". Як var a, b, tmp; a = 1:; b = 2; tmp=a, a=b, b=tmp; Особистий смак.
Джонні Вонг

10

Використовуйте таку третю змінну:

var a = 1,
    b = 2,
    c = a;

a = b; // must be first or a and b end up being both 1
b = c;

DEMO - Використання третьої змінної



10

Метод ES6 +: з ES6 ви можете міняти змінні більш елегантно. Ви можете використовувати зіставлення масиву присвоєння масиву. Це просто. var a = 10 b = 20;

[a, b] = [b, a]

console.log (a, b) // 20 10



9

Ви можете використовувати тимчасову змінну swap або XOR.

a = a ^ b
b = a ^ b
a = a ^ b

Це лише основна логічна концепція і працює всіма мовами, які підтримують операцію XOR.

редагувати: див. коментарі. Забув сказати, що це працює напевно лише з цілим числом. Припущені цілі змінні з потоку питання


16
Працює для програмування інтерв'ю та інших загальних справ. Зауважте, однак, це досить дурний спосіб змінити цінності в реальному житті. З одного боку, в JS він працює лише з цілими числами.
cHao

1
@Kay Що ви маєте на увазі під "не реальною справою за останні 30 років?" Я використовую побітові оператори, коли це має сенс, що насправді досить часто (перемикання невідомого булевого, наприклад)
Бен Гарольд

1
Оператор @cHao Bitwise постійно (і на сьогоднішній день) найшвидший на моїй машині: jsperf.com/swap-array-vs-variable/2
Ben Harold

3
@php_surgeon Я трохи змінив скрипку, щоб компілятор не міг позначити змінні як мертві. jsperf.com/swap-array-vs-variable/3 . Рішення temp var тепер на 1/4 швидше, ніж рішення xor swap
kay - SE - зло

1
@Kay У ситуаціях, коли щось стабільне, добре коментоване, найшвидша техніка і використовується лише в одному-двох місцях, я не бачу, чому це не було б добре у виробничому коді. Бітові операційні системи в цілому є складним поняттям, тому, якщо в коді вже використовується купа з них, технічному обслуговуючому персоналу потрібно було б ознайомитися з ними в будь-якому випадку. У більшості випадків це, очевидно, надмірність. Я просто думаю, що бланкові заяви не завжди корисні; все має місце, навіть «гото» та «еваль».
Beejor


7

Оскільки ваше запитання було дорогоцінним "Тільки ці змінні, а не будь-які об'єкти", відповідь буде також дорогоцінною:

var a = 1, b = 2

a=a+b;
b=a-b;
a=a-b;

це трюк.

І як сказав Родріго Ассіс, це "може бути коротше"

 b=a+(a=b)-b;

Демо: http://jsfiddle.net/abdennour/2jJQ2/


1
Ті самі помилки, що і у відповіді @ DmiN.
kay - SE зла -

де ви виявляєте недоліки? Це не почесно
Abdennour TOUMI

2
@AbdennourToumi Я думаю, що Кей посилається на те, що ваша відповідь працює лише з цілими числами.
showdev

1
@showdev: Будь ласка, прочитайте ще раз запитання: "Тільки ці змінні, не будь-які об'єкти" .... моя відповідь цінна як питання. Я прошу вас позначити коментар. Я повторюю: це не чесно.
Abdennour TOUMI

1
Ця розмова химерна. @AbdennourTOUMI - змінні не збігаються з цілими числами. Ви можете мати змінні, які вказують на об'єкти, рядки, функції, null тощо
Роб Грант

6

Руйнування ES6:

Використання масиву: [a, b] = [b, a]; // my favorite

Використання об'єкта: {a, b} = {a:b, b:a}; // not bad neither


4

Як ми могли пропустити ці класичні онлайнери

var a = 1, b = 2
a = ({a:b, _:(b=a)}).a;

І

var a = 1, b = 2
a = (_=b,b=a,_);

Останній розкриває глобальну змінну '_', але це не має значення, як типова умова javascript - використовувати її як змінну 'dont care'.


1
У другій є помилка друку. Це повинно бутиa = (_=b,b=a,_);
Mageek

Що означає підкреслення _? Чому вона не потребує декларації?
день

Підкреслення - це лише глобальна назва змінної, її можна замінити будь-якою дійсною. напр. a = (justsomething = b, b = a, justsomething)
Теему Іконен

6
О ні, не використовуйте недекларовані магічні глобальні вари! Це вірний шлях до жахливих помилок у реальному житті.
oriadam

Усі, хто використовує underscore.js , будуть дуже нещасні, якщо спробують другий.
Тед

3

Я бачу тут якусь програму олімпіади з програмування. Ще одне хитро однолінійне рішення:

b = (function(){ a=b; return arguments[0]; })(a);

Fiddle: http://jsfiddle.net/cherniv/4q226/


2
Не потрібно використовувати повільний arguments, просто робіть b = (function (x){ return x; })(a, a=b).
Рубен Верборг

1
@RubenVerborgh так, але з аргументами ми не визначаємо третю змінну!
Чернів

1
Технічно argumentsсписок також буде змінною.
Рубен Верборг

1
Ну, ви призначаєте aдо arguments[0]пропусканням його в якості параметра.
Рубен Верборг

1
@RubenVerborgh так, але ви не створюєте своє, argumentsа його призначення відбувається "за лаштунками"
Чернів


3
var a = 5;
var b = 10;

b = [a, a = b][0];
//or
b = [a, a = b];
b = b[0];

//or
b = [a, b];
a = b[1];
b = b[0];


alert("a=" + a + ',' + "b=" + b);

видаліть або прокоментуйте 2 // або і та запустіть з одним набором коду

http://jsfiddle.net/USdv8/57/


2

Ми можемо поміняти var таким чином:

var val1 =  117,
    val2 = 327;

val2 = val1-val2; 
console.log(val2);
val1 = val1-val2;
console.log(val1);
val2 = val1+val2;
console.log(val2);




1

До ES5, щоб поміняти місцями два числа, ви повинні створити змінну temp, а потім поміняти її. Але в ES6 дуже просто замінити два числа за допомогою деструктуризації масиву. Див. Приклад.

let x,y;
[x,y]=[2,3];
console.log(x,y);      // return 2,3

[x,y]=[y,x];
console.log(x,y);      // return 3,2

1

Оскільки я чую, що цей метод працює повільніше:

b = [a, a = b][0];

Якщо ви плануєте зберігати ваші параметри в об'єкті (або масиві), ця функція повинна працювати:

function swapVars(obj, var1, var2){
    let temp = obj[var1];
    obj[var1] = obj[var2];
    obj[var2] = temp;
}

Використання:

let test = {a: 'test 1', b: 'test 2'};

console.log(test); //output: {a: 'test 1', b: 'test 2'}

swapVars(test, 'a', 'b');

console.log(test); //output: {a: 'test 2', b: 'test 1'}

1

Ми можемо використовувати IIFE для заміни двох значень без додаткових параметрів

var a = 5, b =8;
b = (function(a){ 
    return a 
}(a, a=b));

document.write("a: " + a+ "  b:  "+ b);


1

Руйнування масиву ES6 використовується для заміни двох змінних. Див. Приклад

var [x,y]=[1,2];
[x,y]=[y,x];

Простіший спосіб можливий за допомогою:

x === 1і y === 2; Але після руйнування xє y, тобто є 2і yє x, тобто 1.


1

Міняйте за допомогою побітових

let a = 10;
let b = 20;
a ^= b;
y ^= a;
a ^= b;

Однорядковий своп "за допомогою масиву"

[a, b] = [b, a]

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.