Як повернути рядок на місце в JavaScript?


435

Як ви змінити рядок на місці (або в місці) в JavaScript , коли він передається функції із зворотним твердженням, без використання вбудованих функцій ( .reverse(), і .charAt()т.д.)?


тож вам заборонено використовувати .charAt () для отримання символів рядка?
Ірвін

155
Ви не можете. Рядки JavaScript незмінні, це означає, що пам'ять, виділену кожному, не може бути записана, що робить неможливим перевернення справжнього "на місці".
Півмісяць Свіжий

2
Re: crescentfresh Коментар см stackoverflow.com/questions/51185 / ...
baudtack

1
@crescentfresh слід опублікувати це як нову відповідь.
бодотака

Відповіді:


736

Поки ви маєте справу з простими символами ASCII, і ви раді використовувати вбудовані функції, це буде працювати:

function reverse(s){
    return s.split("").reverse().join("");
}

Якщо вам потрібно рішення, яке підтримує UTF-16 або інші багатобайтові символи, пам’ятайте, що ця функція надасть недійсні рядки Unicode або дійсні рядки, які виглядають смішно. Ви можете замість цього розглянути цю відповідь .

[... s] знає Unicode, невелика редакція дає:

function reverse(s){
    return [...s].reverse().join("");
}

44
Це порушено для рядків UTF-16, які містять сурогатні пари, тобто символи поза базовою багатомовною площиною. Це також дасть забавні результати для рядків, що містять комбіновані символи, наприклад, діарез може з’явитися на наступному символі. Перший випуск призведе до недійсних рядків Unicode, другий - до дійсних рядків, які виглядають смішно.
Мартін Пробст

2
@ Richeve Bebedor "Все без використання вбудованих функцій?. Reverse ()" Це не було б прийнятим рішенням, оскільки воно не входить у межі питання, незважаючи на те, що є життєздатним рішенням для перетворення рядка в JS.
Девід Старкі

1
@DavidStarkey: Так, озираючись на це майже через чотири роки, важко зрозуміти, як я так ретельно пропустив суть питання. Схоже, я просто зачекав дві хвилини і схвалив коментар півмісяця до оригінальної публікації!
белаква

14
@MartinProbst Мого відповіді забезпечує Юнікод вирішення проблеми , яка має справу з сурогатними парами і поєднують знаками коректно: stackoverflow.com/a/16776380/96656
Матіас Bynens

1
Для UTF-16 return [...s].reverse().join("");може працювати.
user4642212

411

Для обертання рядка в JavaScript зазвичай використовується наступна техніка (або схожа):

// Don’t use this!
var naiveReverse = function(string) {
    return string.split('').reverse().join('');
}

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

naiveReverse('foo 𝌆 bar');
// → 'rab �� oof'
// Where did the `𝌆` symbol go? Whoops!

Якщо вам цікаво, чому це відбувається, прочитайте про внутрішнє кодування символів JavaScript . (TL; DR: 𝌆це астральний символ, а JavaScript розкриває його як дві окремі одиниці коду.)

Але є ще:

// To see which symbols are being used here, check:
// http://mothereff.in/js-escapes#1ma%C3%B1ana%20man%CC%83ana
naiveReverse('mañana mañana');
// → 'anãnam anañam'
// Wait, so now the tilde is applied to the `a` instead of the `n`? WAT.

Хорошим рядком для тестування зворотних реалізацій рядків є наступне :

'foo 𝌆 bar mañana mañana'

Чому? Оскільки він містить астральний символ ( 𝌆) (який представлений сурогатними парами в JavaScript ) та об'єднувальний знак ( останнійmañana насправді складається з двох символів: U + 006E LATIN SMALL LETTER N та U + 0303 COMBINING TILDE).

Порядок, в якому з’являються сурогатні пари, неможливо змінити, інакше астральний символ більше не відображатиметься в рядку «перевернутий». Ось чому ви побачили ці ��позначки у висновку для попереднього прикладу.

Комбінуючі позначки завжди наносяться на попередній символ, тому ви повинні розглядати як основний символ (U + 006E LATIN SMALL LETTER N) як комбінуючий знак (U + 0303 COMBINING TILDE) в цілому. Повернення їх порядку призведе до того, що знак поєднання буде поєднаний з іншим символом у рядку. Ось чому приклад виведення мав замість ñ.

Сподіваємось, це пояснює, чому всі відповіді, розміщені до цих пір, неправильні .


Щоб відповісти на ваше первинне запитання - як [правильно] повернути рядок у JavaScript, - я написав невелику бібліотеку JavaScript, яка здатна змінити рядки, відомі Unicode. У ньому немає жодного з питань, про які я щойно згадував. Бібліотека називається Esrever ; його код знаходиться на GitHub, і він працює в майже будь-якому середовищі JavaScript. Він постачається з утилітою / бінарною оболонкою, тому ви можете легко повернути рядки зі свого терміналу, якщо хочете.

var input = 'foo 𝌆 bar mañana mañana';
esrever.reverse(input);
// → 'anañam anañam rab 𝌆 oof'

Щодо частини "на місці", дивіться інші відповіді.


65
Ви повинні включити у свою відповідь основну частину коду Есревера.
r0estir0bbe

1
@Meglio При такому конкретному підході, так.
Mathias Bynens

8
Проблема, звичайно, полягає в тому, що "зворотний рядок" звучить однозначно, але це не стикається з проблемами, згаданими тут. Чи повернення рядка повертає рядок, яка при друкуванні відображатиме кластери графеми в рядку у зворотному порядку? З одного боку, це звучить ймовірно. З іншого боку, чому б ви хотіли цього робити? Це визначення залежить від друку, а друк перевернутого рядка рідко корисний. Як частина алгоритму, ваші вимоги можуть бути зовсім різними.
Мартійн

19
Хоча це чудово справляється з поясненням проблеми, фактична відповідь знаходиться в іншому замку . Як говорив @ r0estir0bbe понад рік тому, відповідний код повинен бути у відповіді, а не просто пов'язаний.
TJ Crowder

4
"Сподіваємось, це пояснює, чому всі відповіді, розміщені до цих пір, невірні" - Це твердження є надмірно сильним. У багатьох випадках використання не потрібна підтримка UTF-16 (простий приклад; робота з URL-адресами та компонентами / параметрами URL-адреси). Рішення не є "неправильним" просто тому, що воно не обробляє необов'язковий сценарій. Зокрема, відповідь, що голосує в прямому ефірі, чітко заявляє, що він працює лише з символами ASCII, і, отже, точно навіть не трохи помиляється.
aroth

92
String.prototype.reverse_string=function() {return this.split("").reverse().join("");}

або

String.prototype.reverse_string = function() {
    var s = "";
    var i = this.length;
    while (i>0) {
        s += this.substring(i-1,i);
        i--;
    }
    return s;
}

Я безумовно згоден з прототипом String.
Jeff Meatball Ян

3
конкатенація струн коштує дорого. Краще побудувати масив і приєднатись до нього або використовувати concat ().
Бйорн

2
№1 найкраще, # 2 може бути жахливо повільним
adamJLev

9
Однак жодне рішення не працює, коли символи з'єднаних Unicode відсутні.
Eric Grange

2
@JuanMendes Я залишив цей коментар у 2009 році, все змінилося за останні 4 роки. : P
Bjorn

63

Детальний аналіз та десять різних способів повернути рядок та деталі їх продуктивності.

http://eddmann.com/posts/ten-ways-to-reverse-a-string-in-javascript/

Виконання цих реалізацій:

Найефективніші впровадження для кожного браузера

  • Chrome 15 - Впровадження 1 і 6
  • Firefox 7 - Впровадження 6
  • IE 9 - Впровадження 4
  • Опера 12 - Впровадження 9

Ось такі реалізації:

Впровадження 1:

function reverse(s) {
  var o = '';
  for (var i = s.length - 1; i >= 0; i--)
    o += s[i];
  return o;
}

Впровадження 2:

function reverse(s) {
  var o = [];
  for (var i = s.length - 1, j = 0; i >= 0; i--, j++)
    o[j] = s[i];
  return o.join('');
}

Виконання 3:

function reverse(s) {
  var o = [];
  for (var i = 0, len = s.length; i <= len; i++)
    o.push(s.charAt(len - i));
  return o.join('');
}

Впровадження 4:

function reverse(s) {
  return s.split('').reverse().join('');
}

Впровадження 5:

function reverse(s) {
  var i = s.length,
      o = '';
  while (i > 0) {
    o += s.substring(i - 1, i);
    i--;
  }
  return o;
}

Реалізація 6:

function reverse(s) {
  for (var i = s.length - 1, o = ''; i >= 0; o += s[i--]) { }
  return o;
}

Впровадження 7:

function reverse(s) {
  return (s === '') ? '' : reverse(s.substr(1)) + s.charAt(0);
}

Реалізація 8:

function reverse(s) {
  function rev(s, len, o) {
    return (len === 0) ? o : rev(s, --len, (o += s[len]));
  };
  return rev(s, s.length, '');
}

Впровадження 9:

function reverse(s) {
  s = s.split('');
  var len = s.length,
      halfIndex = Math.floor(len / 2) - 1,
      tmp;


     for (var i = 0; i <= halfIndex; i++) {
        tmp = s[len - i - 1];
        s[len - i - 1] = s[i];
        s[i] = tmp;
      }
      return s.join('');
    }

Впровадження 10

function reverse(s) {
  if (s.length < 2)
    return s;
  var halfIndex = Math.ceil(s.length / 2);
  return reverse(s.substr(halfIndex)) +
         reverse(s.substr(0, halfIndex));
}

53

Весь "зворотний рядок на місці" - це стародавнє питання інтерв'ю програмістів C, і люди, які опитали їх (для помсти, можливо?), Запитають. На жаль, це частина "In Place" вже не працює, оскільки рядки в майже будь-якій керованій мові (JS, C # тощо) використовують незмінні рядки, тим самим перемагаючи всю ідею переміщення рядка, не виділяючи жодної нової пам'яті.

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

Особисто я дуже ненавиджу подібні питання інтерв'ю, але, на жаль, я впевнений, що ми продовжуємо їх бачити на довгі роки.


7
Я, принаймні, можу сказати, що в мене один інтерв'юєр час був дуже вражений, коли він запитав мене, як повернути рядок "на місці" в JS, і я пояснив, чому це неможливо, оскільки рядки в JS незмінні. Я не знаю, чи це відповідь він очікував, чи я трохи навчив його. У будь-якому випадку все вийшло нормально;)
Шев

1
Може бути, він означає "керований" сміттєзбірником, принаймні, це те, що зазвичай розуміється під "керованою мовою" або наявністю віртуальної машини / віртуального середовища виконання? @torazaburo
AntonB

39

Спочатку використовуйте, Array.from()щоб перетворити рядок у масив, потім Array.prototype.reverse()повернути масив, а потім Array.prototype.join()повернути його рядок.

const reverse = str => Array.from(str).reverse().join('');

Це накладні витрати, але це елегантне рішення! Немає перепису попередньої reverseлогіки.
Гершом

2
@felixfbecker Ні, string.split('')не працює. Дивіться цю відповідь для отримання додаткових пояснень.
Michał Perłakowski

5
Це має бути прийнятою відповіддю, оскільки вона також працює з unicode. Наприклад, із прикладу, наведеного вище:Array.from('foo 𝌆 bar mañana mañana').reverse().join('') == 'anãnam anañam rab 𝌆 oof'
Julian TF

3
@JulianTF Не зовсім так, один тильд все ще застосовується до 'a' замість 'n'.
Роман Бойко

2
@RomanBoiko Щоправда, але ви можете спочатку нормалізувати рядок. Array.from('foo 𝌆 bar mañana mañana'.normalize('NFC')).reverse().join('')стане"anañam anañam rab 𝌆 oof"
Містер Лістер

26

У ECMAScript 6 ви можете змінити рядок ще швидше, не використовуючи .split('')метод розділення, за допомогою оператора розповсюдження так:

var str = [...'racecar'].reverse().join('');

1
ES6 також дозволяє використовувати два зворотних посилання `` замість('')

у цьому випадку немає жодних причин використовувати дві підставки
Вік

1
Якщо ви не кодуєте гольф, вам слід цього уникати. Писання string.split('')більш зрозуміло для більшості людей, ніж [...string].
AnnanFay

1
@AnnanFay .split('')має проблему з символами з додаткових площин (сурогатних пар в UTF-16), оскільки він розбивається на кодову одиницю UTF-16, а не на кодову точку . Оператор розповсюдження та Array.from()(на мою перевагу) цього не роблять.
Інклінг

@Inkling Я не розумів, що це проблема. Дякуємо, що вказали на це. Я б все-таки спокусився написати функцію корисності для наочності.
AnnanFay

19

Здається, я запізнився на 3 роки на вечірку ...

На жаль, ви не можете, як було зазначено. Див. Чи не змінюються рядки JavaScript? Чи потрібен "конструктор струн" у JavaScript?

Наступне найкраще, що ви можете зробити - це створити "view" або "обгортку", яка займає рядок і повторно реалізує всі частини API рядка, якими ви користуєтесь, але прикидаючи, що рядок є зворотним. Наприклад:

var identity = function(x){return x};

function LazyString(s) {
    this.original = s;

    this.length = s.length;
    this.start = 0; this.stop = this.length; this.dir = 1; // "virtual" slicing
    // (dir=-1 if reversed)

    this._caseTransform = identity;
}

// syntactic sugar to create new object:
function S(s) {
    return new LazyString(s);
}

//We now implement a `"...".reversed` which toggles a flag which will change our math:

(function(){ // begin anonymous scope
    var x = LazyString.prototype;

    // Addition to the String API
    x.reversed = function() {
        var s = new LazyString(this.original);

        s.start = this.stop - this.dir;
        s.stop = this.start - this.dir;
        s.dir = -1*this.dir;
        s.length = this.length;

        s._caseTransform = this._caseTransform;
        return s;
    }

//We also override string coercion for some extra versatility (not really necessary):

    // OVERRIDE STRING COERCION
    //   - for string concatenation e.g. "abc"+reversed("abc")
    x.toString = function() {
        if (typeof this._realized == 'undefined') {  // cached, to avoid recalculation
            this._realized = this.dir==1 ?
                this.original.slice(this.start,this.stop) : 
                this.original.slice(this.stop+1,this.start+1).split("").reverse().join("");

            this._realized = this._caseTransform.call(this._realized, this._realized);
        }
        return this._realized;
    }

//Now we reimplement the String API by doing some math:

    // String API:

    // Do some math to figure out which character we really want

    x.charAt = function(i) {
        return this.slice(i, i+1).toString();
    }
    x.charCodeAt = function(i) {
        return this.slice(i, i+1).toString().charCodeAt(0);
    }

// Slicing functions:

    x.slice = function(start,stop) {
        // lazy chaining version of https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/slice

        if (stop===undefined)
            stop = this.length;

        var relativeStart = start<0 ? this.length+start : start;
        var relativeStop = stop<0 ? this.length+stop : stop;

        if (relativeStart >= this.length)
            relativeStart = this.length;
        if (relativeStart < 0)
            relativeStart = 0;

        if (relativeStop > this.length)
            relativeStop = this.length;
        if (relativeStop < 0)
            relativeStop = 0;

        if (relativeStop < relativeStart)
            relativeStop = relativeStart;

        var s = new LazyString(this.original);
        s.length = relativeStop - relativeStart;
        s.start = this.start + this.dir*relativeStart;
        s.stop = s.start + this.dir*s.length;
        s.dir = this.dir;

        //console.log([this.start,this.stop,this.dir,this.length], [s.start,s.stop,s.dir,s.length])

        s._caseTransform = this._caseTransform;
        return s;
    }
    x.substring = function() {
        // ...
    }
    x.substr = function() {
        // ...
    }

//Miscellaneous functions:

    // Iterative search

    x.indexOf = function(value) {
        for(var i=0; i<this.length; i++)
            if (value==this.charAt(i))
                return i;
        return -1;
    }
    x.lastIndexOf = function() {
        for(var i=this.length-1; i>=0; i--)
            if (value==this.charAt(i))
                return i;
        return -1;
    }

    // The following functions are too complicated to reimplement easily.
    // Instead just realize the slice and do it the usual non-in-place way.

    x.match = function() {
        var s = this.toString();
        return s.apply(s, arguments);
    }
    x.replace = function() {
        var s = this.toString();
        return s.apply(s, arguments);
    }
    x.search = function() {
        var s = this.toString();
        return s.apply(s, arguments);
    }
    x.split = function() {
        var s = this.toString();
        return s.apply(s, arguments);
    }

// Case transforms:

    x.toLowerCase = function() {
        var s = new LazyString(this.original);
        s._caseTransform = ''.toLowerCase;

        s.start=this.start; s.stop=this.stop; s.dir=this.dir; s.length=this.length;

        return s;
    }
    x.toUpperCase = function() {
        var s = new LazyString(this.original);
        s._caseTransform = ''.toUpperCase;

        s.start=this.start; s.stop=this.stop; s.dir=this.dir; s.length=this.length;

        return s;
    }

})() // end anonymous scope

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

> r = S('abcABC')
LazyString
  original: "abcABC"
  __proto__: LazyString

> r.charAt(1);       // doesn't reverse string!!! (good if very long)
"B"

> r.toLowerCase()    // must reverse string, so does so
"cbacba"

> r.toUpperCase()    // string already reversed: no extra work
"CBACBA"

> r + '-demo-' + r   // natural coercion, string already reversed: no extra work
"CBAcba-demo-CBAcba"

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

> 'demo: ' + S('0123456789abcdef').slice(3).reversed().slice(1,-1).toUpperCase()
"demo: EDCBA987654"

> S('0123456789ABCDEF').slice(3).reversed().slice(1,-1).toLowerCase().charAt(3)
"b"

Це дає значну економію, якщо застосовувати її до дуже великої струни, якщо ви берете лише порівняно невеликий шматочок.

Чи варто цього вартий (над реверсіруемой копією, як у більшості мов програмування) сильно залежить від вашого випадку використання та того, наскільки ефективно ви повторно реалізуєте API рядка. Наприклад, якщо все, що вам потрібно, це зробити маніпуляцію з індексами рядків або взяти невеликі slices або substrs, це заощадить ваш простір та час. Якщо ви плануєте друкувати великі перевернуті фрагменти або підрядки, то економія може бути дійсно невеликою, навіть гіршою, ніж зробити повну копію. Ваш "обернений" рядок також не буде мати тип string, хоча ви, можливо, зможете підробити це під час прототипування.

Вищеописана демонстраційна реалізація створює новий об'єкт типу ReversedString. Він є прототипом і, отже, досить ефективним, з майже мінімальною роботою та мінімальними витратами на простір (визначення прототипу є спільними). Це ледача реалізація, що передбачає відкладені нарізки. Кожен раз, коли ви виконуєте функцію на кшталт .sliceабо .reversed, вона виконуватиме математику індексу. Нарешті, коли ви виймаєте дані (неявно зателефонувавши .toString()або .charCodeAt(...)щось таке), вони застосовуватимуться "розумним" способом, торкаючись найменш можливих даних.

Примітка. Наведений вище рядок API є прикладом, і він не може бути реалізований ідеально. Ви також можете використовувати лише 1-2 функції, які вам потрібні.


13

Існує багато способів змінити рядок у JavaScript. Я записую три способи, які я віддаю перевагу.

Підхід 1: Використання функції зворотного зв'язку:

function reverse(str) {
  return str.split('').reverse().join('');
}

Підхід 2: перегляд символів:

function reverse(str) {
  let reversed = '';

  for (let character of str) {
    reversed = character + reversed;
  }

  return reversed;
}

Підхід 3: Використання функції зменшення:

function reverse(str) {
  return str.split('').reduce((rev, char) => char + rev, '');
}

Сподіваюся, це допомагає :)


10

Під час інтерв'ю мене попросили повернути рядок, не використовуючи змінних або нативних методів. Це моя улюблена реалізація:

function reverseString(str) {
    return str === '' ? '' : reverseString(str.slice(1)) + str[0];
}

Короткий, простий, але повільний, як пекло;)
Том,

13
Нульові рідні методи? Про що slice? : - /
листопад

1
Цікаве використання рекурсії. Іронічно, що це на Stack Overflow. stackoverflow.com/q/2805172/265877
Олексій

@ Алекс, ти добре зазначаєш. У деяких випадках інтерв'юер попросить вас не використовувати Array.prototype.reverse().
Даніель

10

Існує кілька способів зробити це, ви можете перевірити наступне,

1. Традиційні для циклу (збільшення):

function reverseString(str){
        let stringRev ="";
        for(let i= 0; i<str.length; i++){
            stringRev = str[i]+stringRev;
        }
        return stringRev;
}
alert(reverseString("Hello World!"));

2. Традиційні для циклу (декрементація):

function reverseString(str){
    let revstr = "";
    for(let i = str.length-1; i>=0; i--){
        revstr = revstr+ str[i];
    }
    return revstr;
}
alert(reverseString("Hello World!"));

3. Використання циклу for-of

function reverseString(str){
    let strn ="";
    for(let char of str){
        strn = char + strn;
    }
    return strn;
}
alert(reverseString("Get well soon"));

4. Використовуючи метод масиву forEach / high order:

function reverseString(str){

  let revSrring = "";
  str.split("").forEach(function(char){
    
    revSrring = char + revSrring;
  
  });
  return revSrring;
}
alert(reverseString("Learning JavaScript"));

5. Стандарт ES6:

function reverseString(str){

  let revSrring = "";
  str.split("").forEach(char => revSrring = char + revSrring);
  return revSrring;
}
alert(reverseString("Learning JavaScript"));

6. Останній спосіб:

function reverseString(str){

  return str.split("").reduce(function(revString, char){
       return char + revString;
  }, "");
 
}

alert(reverseString("Learning JavaScript"));

7. Ви також можете отримати результат, використовуючи наступне,

function reverseString(str){

  return str.split("").reduce((revString, char)=> char + revString, "");
 
}
alert(reverseString("Learning JavaScript"));



6

Це найпростіший спосіб, на який я думаю

var reverse = function(str) {
    var arr = [];
    
    for (var i = 0, len = str.length; i <= len; i++) {
        arr.push(str.charAt(len - i))
    }

    return arr.join('');
}

console.log(reverse('I want a 🍺'));


3
Приємно, що ви включили смайлик у свій приклад. Так що ми швидко бачимо, що це явно не працює для емоджи та багатьох інших символів унікоду.
Íhor Mé

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

6
var str = 'sample string';
[].map.call(str, function(x) {
  return x;
}).reverse().join('');

АБО

var str = 'sample string';
console.log(str.split('').reverse().join(''));

// Вихід: 'gnirts elpmas'


Усю частину «карти» можна записати як [...str].

5

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

function StringReverse (str)
{
  var charArray = [];
  for (var i = 0; i < str.length; i++)
    {
      if (i+1 < str.length)
        {
          var value = str.charCodeAt(i);
          var nextValue = str.charCodeAt(i+1);
          if (   (   value >= 0xD800 && value <= 0xDBFF
                  && (nextValue & 0xFC00) == 0xDC00) // Surrogate pair)
              || (nextValue >= 0x0300 && nextValue <= 0x036F)) // Combining marks
            {
              charArray.unshift(str.substring(i, i+2));
              i++; // Skip the other half
              continue;
            }
        }

      // Otherwise we just have a rogue surrogate marker or a plain old character.
      charArray.unshift(str[i]);
    }

  return charArray.join('');
}

Усі реквізити до Mathias, Punycode та різні інші посилання на навчання мені про складність кодування символів у JavaScript.



3

Якщо ви не хочете використовувати будь-яку вбудовану функцію. Спробуйте це

var string = 'abcdefg';
var newstring = '';

for(let i = 0; i < string.length; i++){
    newstring = string[i] += newstring;
}

console.log(newstring);

2

Справжня відповідь: ви не можете змінити її на місці, але ви можете створити новий рядок, який є зворотним.

Як вправа грати з рекурсією: іноді, коли ви йдете на співбесіду, інтерв'юер може запитати вас, як це зробити за допомогою рекурсії, і я думаю, що "бажана відповідь" може бути ", я б краще не робив цього в рекурсії, як це може легко спричинити переповнення стека "(тому, що це O(n)швидше, ніж O(log n). Якщо це так O(log n), досить складно отримати переповнення стека - 4 мільярди елементів можна було б обробити рівнем стека 32, оскільки 2 ** 32 становить 4294967296. Але якщо це так O(n), то він може легко отримати переповнення стека.

Іноді інтерв'ю все ще запитає у вас, "як вправу, чому б ви все-таки не написали це за допомогою рекурсії?" І ось це:

String.prototype.reverse = function() {
    if (this.length <= 1) return this;
    else return this.slice(1).reverse() + this.slice(0,1);
}

пробіг:

var s = "";
for(var i = 0; i < 1000; i++) {
    s += ("apple" + i);
}
console.log(s.reverse());

вихід:

999elppa899elppa...2elppa1elppa0elppa

Щоб спробувати отримати переповнення стека, я змінив 1000на 10000Google Chrome, і він повідомив:

RangeError: Maximum call stack size exceeded

2

Самі рядки незмінні, але ви можете легко створити зворотну копію з наступним кодом:

function reverseString(str) {

  var strArray = str.split("");
  strArray.reverse();

  var strReverse = strArray.join("");

  return strReverse;
}

reverseString("hello");

2
//es6
//array.from
const reverseString = (string) => Array.from(string).reduce((a, e) => e + a);
//split
const reverseString = (string) => string.split('').reduce((a, e) => e + a); 

//split problem
"𠜎𠺢".split('')[0] === Array.from("𠜎𠺢")[0] // "�" === "𠜎" => false
"😂😹🤗".split('')[0] === Array.from("😂😹🤗")[0] // "�" === "😂" => false

1
Це має перевагу в тому, що вона правильно поводиться з додатковими символами площини.

2

Невелика функція, яка поєднує діакритику та двобайтові символи:

(function(){
  var isCombiningDiacritic = function( code )
  {
    return (0x0300 <= code && code <= 0x036F)  // Comb. Diacritical Marks
        || (0x1AB0 <= code && code <= 0x1AFF)  // Comb. Diacritical Marks Extended
        || (0x1DC0 <= code && code <= 0x1DFF)  // Comb. Diacritical Marks Supplement
        || (0x20D0 <= code && code <= 0x20FF)  // Comb. Diacritical Marks for Symbols
        || (0xFE20 <= code && code <= 0xFE2F); // Comb. Half Marks

  };

  String.prototype.reverse = function()
  {
    var output = "",
        i      = this.length - 1,
        width;

    for ( ; i >= 0; --i )
    {
      width = 1;
      while( i > 0 && isCombiningDiacritic( this.charCodeAt(i) ) )
      {
        --i;
        width++;
      }

      if (
           i > 0
        && "\uDC00" <= this[i]   && this[i]   <= "\uDFFF"
        && "\uD800" <= this[i-1] && this[i-1] <= "\uDBFF"
      )
      {
        --i;
        width++;
      }

      output += this.substr( i, width );
    }

    return output;
  }
})();

// Tests
[
  'abcdefg',
  'ab\u0303c',
  'a\uD83C\uDFA5b',
  'a\uD83C\uDFA5b\uD83C\uDFA6c',
  'a\uD83C\uDFA5b\u0306c\uD83C\uDFA6d',
  'TO͇̹̺ͅƝ̴ȳ̳ TH̘Ë͖́̉ ͠P̯͍̭O̚​N̐Y̡' // copied from http://stackoverflow.com/a/1732454/1509264
].forEach(
  function(str){ console.log( str + " -> " + str.reverse() ); }
);
  


Оновлення

Більш повний перелік об'єднаних діакритиків:

      var isCombiningDiacritic = function( code )
      {
        return (0x0300 <= code && code <= 0x036F)
            || (0x0483 <= code && code <= 0x0489)
            || (0x0591 <= code && code <= 0x05BD)
            || (code == 0x05BF)
            || (0x05C1 <= code && code <= 0x05C2)
            || (0x05C4 <= code && code <= 0x05C5)
            || (code == 0x05C7)
            || (0x0610 <= code && code <= 0x061A)
            || (0x064B <= code && code <= 0x065F)
            || (code == 0x0670)
            || (0x06D6 <= code && code <= 0x06DC)
            || (0x06DF <= code && code <= 0x06E4)
            || (0x06E7 <= code && code <= 0x06E8)
            || (0x06EA <= code && code <= 0x06ED)
            || (code == 0x0711)
            || (0x0730 <= code && code <= 0x074A)
            || (0x07A6 <= code && code <= 0x07B0)
            || (0x07EB <= code && code <= 0x07F3)
            || (code == 0x07FD)
            || (0x0816 <= code && code <= 0x0819)
            || (0x081B <= code && code <= 0x0823)
            || (0x0825 <= code && code <= 0x0827)
            || (0x0829 <= code && code <= 0x082D)
            || (0x0859 <= code && code <= 0x085B)
            || (0x08D3 <= code && code <= 0x08E1)
            || (0x08E3 <= code && code <= 0x0902)
            || (code == 0x093A)
            || (code == 0x093C)
            || (0x0941 <= code && code <= 0x0948)
            || (code == 0x094D)
            || (0x0951 <= code && code <= 0x0957)
            || (0x0962 <= code && code <= 0x0963)
            || (code == 0x0981)
            || (code == 0x09BC)
            || (0x09C1 <= code && code <= 0x09C4)
            || (code == 0x09CD)
            || (0x09E2 <= code && code <= 0x09E3)
            || (0x09FE <= code && code <= 0x0A02)
            || (code == 0x0A3C)
            || (0x0A41 <= code && code <= 0x0A51)
            || (0x0A70 <= code && code <= 0x0A71)
            || (code == 0x0A75)
            || (0x0A81 <= code && code <= 0x0A82)
            || (code == 0x0ABC)
            || (0x0AC1 <= code && code <= 0x0AC8)
            || (code == 0x0ACD)
            || (0x0AE2 <= code && code <= 0x0AE3)
            || (0x0AFA <= code && code <= 0x0B01)
            || (code == 0x0B3C)
            || (code == 0x0B3F)
            || (0x0B41 <= code && code <= 0x0B44)
            || (0x0B4D <= code && code <= 0x0B56)
            || (0x0B62 <= code && code <= 0x0B63)
            || (code == 0x0B82)
            || (code == 0x0BC0)
            || (code == 0x0BCD)
            || (code == 0x0C00)
            || (code == 0x0C04)
            || (0x0C3E <= code && code <= 0x0C40)
            || (0x0C46 <= code && code <= 0x0C56)
            || (0x0C62 <= code && code <= 0x0C63)
            || (code == 0x0C81)
            || (code == 0x0CBC)
            || (0x0CCC <= code && code <= 0x0CCD)
            || (0x0CE2 <= code && code <= 0x0CE3)
            || (0x0D00 <= code && code <= 0x0D01)
            || (0x0D3B <= code && code <= 0x0D3C)
            || (0x0D41 <= code && code <= 0x0D44)
            || (code == 0x0D4D)
            || (0x0D62 <= code && code <= 0x0D63)
            || (code == 0x0DCA)
            || (0x0DD2 <= code && code <= 0x0DD6)
            || (code == 0x0E31)
            || (0x0E34 <= code && code <= 0x0E3A)
            || (0x0E47 <= code && code <= 0x0E4E)
            || (code == 0x0EB1)
            || (0x0EB4 <= code && code <= 0x0EBC)
            || (0x0EC8 <= code && code <= 0x0ECD)
            || (0x0F18 <= code && code <= 0x0F19)
            || (code == 0x0F35)
            || (code == 0x0F37)
            || (code == 0x0F39)
            || (0x0F71 <= code && code <= 0x0F7E)
            || (0x0F80 <= code && code <= 0x0F84)
            || (0x0F86 <= code && code <= 0x0F87)
            || (0x0F8D <= code && code <= 0x0FBC)
            || (code == 0x0FC6)
            || (0x102D <= code && code <= 0x1030)
            || (0x1032 <= code && code <= 0x1037)
            || (0x1039 <= code && code <= 0x103A)
            || (0x103D <= code && code <= 0x103E)
            || (0x1058 <= code && code <= 0x1059)
            || (0x105E <= code && code <= 0x1060)
            || (0x1071 <= code && code <= 0x1074)
            || (code == 0x1082)
            || (0x1085 <= code && code <= 0x1086)
            || (code == 0x108D)
            || (code == 0x109D)
            || (0x135D <= code && code <= 0x135F)
            || (0x1712 <= code && code <= 0x1714)
            || (0x1732 <= code && code <= 0x1734)
            || (0x1752 <= code && code <= 0x1753)
            || (0x1772 <= code && code <= 0x1773)
            || (0x17B4 <= code && code <= 0x17B5)
            || (0x17B7 <= code && code <= 0x17BD)
            || (code == 0x17C6)
            || (0x17C9 <= code && code <= 0x17D3)
            || (code == 0x17DD)
            || (0x180B <= code && code <= 0x180D)
            || (0x1885 <= code && code <= 0x1886)
            || (code == 0x18A9)
            || (0x1920 <= code && code <= 0x1922)
            || (0x1927 <= code && code <= 0x1928)
            || (code == 0x1932)
            || (0x1939 <= code && code <= 0x193B)
            || (0x1A17 <= code && code <= 0x1A18)
            || (code == 0x1A1B)
            || (code == 0x1A56)
            || (0x1A58 <= code && code <= 0x1A60)
            || (code == 0x1A62)
            || (0x1A65 <= code && code <= 0x1A6C)
            || (0x1A73 <= code && code <= 0x1A7F)
            || (0x1AB0 <= code && code <= 0x1B03)
            || (code == 0x1B34)
            || (0x1B36 <= code && code <= 0x1B3A)
            || (code == 0x1B3C)
            || (code == 0x1B42)
            || (0x1B6B <= code && code <= 0x1B73)
            || (0x1B80 <= code && code <= 0x1B81)
            || (0x1BA2 <= code && code <= 0x1BA5)
            || (0x1BA8 <= code && code <= 0x1BA9)
            || (0x1BAB <= code && code <= 0x1BAD)
            || (code == 0x1BE6)
            || (0x1BE8 <= code && code <= 0x1BE9)
            || (code == 0x1BED)
            || (0x1BEF <= code && code <= 0x1BF1)
            || (0x1C2C <= code && code <= 0x1C33)
            || (0x1C36 <= code && code <= 0x1C37)
            || (0x1CD0 <= code && code <= 0x1CD2)
            || (0x1CD4 <= code && code <= 0x1CE0)
            || (0x1CE2 <= code && code <= 0x1CE8)
            || (code == 0x1CED)
            || (code == 0x1CF4)
            || (0x1CF8 <= code && code <= 0x1CF9)
            || (0x1DC0 <= code && code <= 0x1DFF)
            || (0x20D0 <= code && code <= 0x20F0)
            || (0x2CEF <= code && code <= 0x2CF1)
            || (code == 0x2D7F)
            || (0x2DE0 <= code && code <= 0x2DFF)
            || (0x302A <= code && code <= 0x302D)
            || (0x3099 <= code && code <= 0x309A)
            || (0xA66F <= code && code <= 0xA672)
            || (0xA674 <= code && code <= 0xA67D)
            || (0xA69E <= code && code <= 0xA69F)
            || (0xA6F0 <= code && code <= 0xA6F1)
            || (code == 0xA802)
            || (code == 0xA806)
            || (code == 0xA80B)
            || (0xA825 <= code && code <= 0xA826)
            || (0xA8C4 <= code && code <= 0xA8C5)
            || (0xA8E0 <= code && code <= 0xA8F1)
            || (code == 0xA8FF)
            || (0xA926 <= code && code <= 0xA92D)
            || (0xA947 <= code && code <= 0xA951)
            || (0xA980 <= code && code <= 0xA982)
            || (code == 0xA9B3)
            || (0xA9B6 <= code && code <= 0xA9B9)
            || (0xA9BC <= code && code <= 0xA9BD)
            || (code == 0xA9E5)
            || (0xAA29 <= code && code <= 0xAA2E)
            || (0xAA31 <= code && code <= 0xAA32)
            || (0xAA35 <= code && code <= 0xAA36)
            || (code == 0xAA43)
            || (code == 0xAA4C)
            || (code == 0xAA7C)
            || (code == 0xAAB0)
            || (0xAAB2 <= code && code <= 0xAAB4)
            || (0xAAB7 <= code && code <= 0xAAB8)
            || (0xAABE <= code && code <= 0xAABF)
            || (code == 0xAAC1)
            || (0xAAEC <= code && code <= 0xAAED)
            || (code == 0xAAF6)
            || (code == 0xABE5)
            || (code == 0xABE8)
            || (code == 0xABED)
            || (code == 0xFB1E)
            || (0xFE00 <= code && code <= 0xFE0F)
            || (0xFE20 <= code && code <= 0xFE2F)
            || (code == 0x101FD)
            || (code == 0x102E0)
            || (0x10376 <= code && code <= 0x1037A)
            || (0x10A01 <= code && code <= 0x10A0F)
            || (0x10A38 <= code && code <= 0x10A3F)
            || (0x10AE5 <= code && code <= 0x10AE6)
            || (0x10D24 <= code && code <= 0x10D27)
            || (0x10F46 <= code && code <= 0x10F50)
            || (code == 0x11001)
            || (0x11038 <= code && code <= 0x11046)
            || (0x1107F <= code && code <= 0x11081)
            || (0x110B3 <= code && code <= 0x110B6)
            || (0x110B9 <= code && code <= 0x110BA)
            || (0x11100 <= code && code <= 0x11102)
            || (0x11127 <= code && code <= 0x1112B)
            || (0x1112D <= code && code <= 0x11134)
            || (code == 0x11173)
            || (0x11180 <= code && code <= 0x11181)
            || (0x111B6 <= code && code <= 0x111BE)
            || (0x111C9 <= code && code <= 0x111CC)
            || (0x1122F <= code && code <= 0x11231)
            || (code == 0x11234)
            || (0x11236 <= code && code <= 0x11237)
            || (code == 0x1123E)
            || (code == 0x112DF)
            || (0x112E3 <= code && code <= 0x112EA)
            || (0x11300 <= code && code <= 0x11301)
            || (0x1133B <= code && code <= 0x1133C)
            || (code == 0x11340)
            || (0x11366 <= code && code <= 0x11374)
            || (0x11438 <= code && code <= 0x1143F)
            || (0x11442 <= code && code <= 0x11444)
            || (code == 0x11446)
            || (code == 0x1145E)
            || (0x114B3 <= code && code <= 0x114B8)
            || (code == 0x114BA)
            || (0x114BF <= code && code <= 0x114C0)
            || (0x114C2 <= code && code <= 0x114C3)
            || (0x115B2 <= code && code <= 0x115B5)
            || (0x115BC <= code && code <= 0x115BD)
            || (0x115BF <= code && code <= 0x115C0)
            || (0x115DC <= code && code <= 0x115DD)
            || (0x11633 <= code && code <= 0x1163A)
            || (code == 0x1163D)
            || (0x1163F <= code && code <= 0x11640)
            || (code == 0x116AB)
            || (code == 0x116AD)
            || (0x116B0 <= code && code <= 0x116B5)
            || (code == 0x116B7)
            || (0x1171D <= code && code <= 0x1171F)
            || (0x11722 <= code && code <= 0x11725)
            || (0x11727 <= code && code <= 0x1172B)
            || (0x1182F <= code && code <= 0x11837)
            || (0x11839 <= code && code <= 0x1183A)
            || (0x119D4 <= code && code <= 0x119DB)
            || (code == 0x119E0)
            || (0x11A01 <= code && code <= 0x11A06)
            || (0x11A09 <= code && code <= 0x11A0A)
            || (0x11A33 <= code && code <= 0x11A38)
            || (0x11A3B <= code && code <= 0x11A3E)
            || (code == 0x11A47)
            || (0x11A51 <= code && code <= 0x11A56)
            || (0x11A59 <= code && code <= 0x11A5B)
            || (0x11A8A <= code && code <= 0x11A96)
            || (0x11A98 <= code && code <= 0x11A99)
            || (0x11C30 <= code && code <= 0x11C3D)
            || (0x11C92 <= code && code <= 0x11CA7)
            || (0x11CAA <= code && code <= 0x11CB0)
            || (0x11CB2 <= code && code <= 0x11CB3)
            || (0x11CB5 <= code && code <= 0x11CB6)
            || (0x11D31 <= code && code <= 0x11D45)
            || (code == 0x11D47)
            || (0x11D90 <= code && code <= 0x11D91)
            || (code == 0x11D95)
            || (code == 0x11D97)
            || (0x11EF3 <= code && code <= 0x11EF4)
            || (0x16AF0 <= code && code <= 0x16AF4)
            || (0x16B30 <= code && code <= 0x16B36)
            || (code == 0x16F4F)
            || (0x16F8F <= code && code <= 0x16F92)
            || (0x1BC9D <= code && code <= 0x1BC9E)
            || (0x1D167 <= code && code <= 0x1D169)
            || (0x1D17B <= code && code <= 0x1D182)
            || (0x1D185 <= code && code <= 0x1D18B)
            || (0x1D1AA <= code && code <= 0x1D1AD)
            || (0x1D242 <= code && code <= 0x1D244)
            || (0x1DA00 <= code && code <= 0x1DA36)
            || (0x1DA3B <= code && code <= 0x1DA6C)
            || (code == 0x1DA75)
            || (code == 0x1DA84)
            || (0x1DA9B <= code && code <= 0x1E02A)
            || (0x1E130 <= code && code <= 0x1E136)
            || (0x1E2EC <= code && code <= 0x1E2EF)
            || (0x1E8D0 <= code && code <= 0x1E8D6)
            || (0x1E944 <= code && code <= 0x1E94A)
            || (0xE0100 <= code && code <= 0xE01EF);
      };

Достойна спроба, але якби ви сканували файл UnicodeData.txt, ви побачите, що існує 316 таких діапазонів, що поєднують діакритику, а не 5.
Містер Лістер,

@MrLister Рішенням цього є редагування isCombiningDiacriticфункції, що включає всі 316 діапазони; не соромтеся надати це редагування, оскільки, здається, у вас є дані.
MT0

1
function reverseString(string) {
    var reversedString = "";
    var stringLength = string.length - 1;
    for (var i = stringLength; i >= 0; i--) {
        reversedString += string[i];
    }
    return reversedString;
}

1

без перетворення рядка в масив;

String.prototype.reverse = function() {

    var ret = "";
    var size = 0;

    for (var i = this.length - 1; -1 < i; i -= size) {

        if (
          '\uD800' <= this[i - 1] && this[i - 1] <= '\uDBFF' && 
          '\uDC00' <= this[i]     && this[i]     <= '\uDFFF'
        ) {
            size = 2;
            ret += this[i - 1] + this[i];
        } else {
            size = 1;
            ret += this[i];
        }
    }

    return ret;
}

console.log('anãnam anañam' === 'mañana mañana'.reverse());

використання Array.reverse без перетворення символів у кодові точки;

String.prototype.reverse = function() {

    var array = this.split("").reverse();

    for (var i = 0; i < this.length; ++i) {

        if (
          '\uD800' <= this[i - 1] && this[i - 1] <= '\uDBFF' && 
          '\uDC00' <= this[i]     && this[i]     <= '\uDFFF'
        ) {
            array[i - 1] = array[i - 1] + array[i];
            array[i] = array[i - 1].substr(0, 1);
            array[i - 1] = array[i - 1].substr(1, 1);
        }

    }

    return array.join("");
}

console.log('anãnam anañam' === 'mañana mañana'.reverse());

Для другої версії: var c = array[i-1]; array[i-1] = array[i]; array[i] = c;не потрібно об'єднання пари коду. Також цикл for-loop повинен починатися з 1.
MT0,

Друга версія не працює з '\ud83c\ud83c\udfa5'.reverse()- вона виведе так само, як і вхід. Додавання ++i;в ifоператор має це виправити.
MT0

Щодо другої думки - це не справляється з поєднанням діакритики: 'a\u0303bc'.reverse() === 'cba\u0303'слід повернути істину.
MT0

1

Я думаю, що String.prototype.reverse - це хороший спосіб вирішити цю проблему; код, як показано нижче;

String.prototype.reverse = function() {
  return this.split('').reverse().join('');
}

var str = 'this is a good example for string reverse';
str.reverse();
-> "esrever gnirts rof elpmaxe doog a si siht";

1

Використовуючи функції масиву,

String.prototype.reverse = function(){
    return [].reduceRight.call(this, function(last, secLast){return last + secLast});
}

1
var str = "my name is saurabh ";
var empStr='',finalString='';
var chunk=[];
function reverse(str){
var i,j=0,n=str.length;
    for(i=0;i<n;++i){
        if(str[i]===' '){
            chunk[j]=empStr;
            empStr = '';
            j++;
        }else{
            empStr=empStr+str[i];
        }
    }
    for(var z=chunk.length-1;z>=0;z--){
        finalString = finalString +' '+ chunk[z];
        console.log(finalString);
    }
    return true;
}
reverse(str);

Як це "на місці" ??
Sudhansu Choudhary

1

Моя власна оригінальна спроба ...

var str = "The Car";

function reverseStr(str) {
  var reversed = "";
  var len = str.length;
  for (var i = 1; i < (len + 1); i++) {  
    reversed += str[len - i];      
  }

  return reversed;
}

var strReverse = reverseStr(str);    
console.log(strReverse);
// "raC ehT"

http://jsbin.com/bujiwo/19/edit?js,console,output


1

Тримайте це ДУХО і просто нерозумно !!

function reverse(s){
let str = s;
var reverse = '';
for (var i=str.length;i>0;i--){

    var newstr = str.substring(0,i)
    reverse += newstr.substr(-1,1)
}
return reverse;
}

1

Добре, досить просто, ви можете створити функцію з допомогою простого циклу , щоб гарантувати рядку зворотної для вас без використання reverse(), і charAt()т.д. , як це:

Наприклад, у вас є ця рядок:

var name = "StackOverflow";

Створіть таку функцію, як я її називаю reverseString...

function reverseString(str) {
  if(!str.trim() || 'string' !== typeof str) {
    return;
  }
  let l=str.length, s='';
  while(l > 0) {
    l--;
    s+= str[l];
  }
  return s;
}

І ви можете назвати це так:

reverseString(name);

І результат буде:

"wolfrevOkcatS"

1

Найкращі способи повернути рядок у JavaScript

1) Array.reverse:

Ви, напевно, думаєте, чекайте, я думав, що ми повернули рядок, чому ви використовуєте метод Array.reverse. Використовуючи метод String.split, ми перетворюємо наш рядок у масив символів. Потім ми реверсуємо порядок кожного значення в масиві, а потім, нарешті, перетворюємо масив назад у рядок за допомогою методу Array.join.

function reverseString(str) {
    return str.split('').reverse().join('');
}
reverseString('dwayne');

2) Зменшення в циклі:

Хоча це досить багатослівне, це рішення має свої переваги перед рішенням. Ви не створюєте масив, а ви просто об'єднуєте рядок на основі символів з вихідного рядка.

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

function reverseString(str) {
    var temp = '';
    var i = str.length;

    while (i > 0) {
        temp += str.substring(i - 1, i);
        i--;
    }


    return temp;
}
reverseString('dwayne');

3) Рекурсія

Мені подобається, наскільки просто і зрозуміло це рішення. Ви чітко бачите, що методи String.charAt і String.substr використовуються для проходження різного значення, викликаючи себе щоразу, поки рядок не буде порожнім, з якого потрійний просто поверне порожню рядок замість того, щоб використовувати рекурсію для виклику себе . Це, ймовірно, призведе до другого найкращого результату після другого рішення.

function reverseString(str) {
    return (str === '') ? '' : reverseString(str.substr(1)) + str.charAt(0);
}
reverseString('dwayne');
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.