Как вы переворачиваете строку в JavaScript?


435

Как вы переворачиваете строку на месте (или на месте) в JavaScript, когда она передается функции с оператором return, без использования встроенных функций ( .reverse()и .charAt()т. Д.)?


Итак, вы не можете использовать .charAt (), чтобы получить символы строки?
Ирвин

155
Ты не можешь Строки JavaScript являются неизменяемыми, что означает, что память, выделенная для каждой из них, не может быть записана, что делает невозможным истинное обращение «на месте».
Crescent Fresh

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

1
@crescentfresh, вы должны опубликовать это как новый ответ.
Бодэкт

Ответы:


737

Пока вы имеете дело с простыми символами ASCII и счастливы использовать встроенные функции, это будет работать:

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

Если вам нужно решение, которое поддерживает UTF-16 или другие многобайтовые символы, имейте в виду, что эта функция выдаст недопустимые строки юникода или допустимые строки, которые выглядят забавно. Возможно, вы захотите рассмотреть этот ответ вместо .

[... s] поддерживает Unicode, небольшое редактирование дает: -

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

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

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

1
@DavidStarkey: Да, оглядываясь назад на это почти четыре года спустя, трудно понять, как я так тщательно пропустил суть вопроса. Похоже, я должен был просто подождать две минуты и отозвать комментарий crescentfresh на исходное сообщение!
Белаква

14
@MartinProbst Мой ответ предоставляет Unicode-ориентированное решение проблемы, которая правильно обрабатывает суррогатные пары и объединяет метки: stackoverflow.com/a/16776380/96656
Матиас Биненс,

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 МАЛЕНЬКОЕ ПИСЬМО N и U + 0303 КОМБИНИРОВАННАЯ ТИЛЬДА).

Порядок, в котором появляются суррогатные пары, изменить нельзя, иначе астральный символ больше не будет отображаться в «перевернутой» строке. Вот почему вы видели эти ��отметки в выходных данных для предыдущего примера.

Комбинированные метки всегда применяются к предыдущему символу, поэтому вы должны рассматривать оба основных символа (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
Вы должны включить в свой ответ основную часть кода Esrever.
r0estir0bbe

1
@ Meglio При таком подходе да.
Матиас Биненс

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.
Джефф Фрикадель Ян

3
Конкатенация строк стоит дорого. Лучше построить массив и присоединиться к нему или использовать concat ().
Бьорн

2
№ 1 - лучший, № 2 - ужасно медленный
adamJLev

9
Однако ни одно из решений не работает, когда присутствуют составные символы Unicode.
Эрик Грандж

2
@JuanMendes Я оставил этот комментарий в 2009 году, все изменилось за последние 4 года. : P
Бьорн

63

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

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

Выполнение этих реализаций:

Наиболее эффективные реализации для каждого браузера

  • Chrome 15 - реализации 1 и 6
  • Firefox 7 - Реализация 6
  • IE 9 - реализация 4
  • Opera 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

Целое «перевернуть строку на месте» - это устаревший вопрос для программистов на С, и люди, которые брали у них интервью (возможно, для мести?), Спросят. К сожалению, это часть «На месте», которая больше не работает, потому что строки практически в любом управляемом языке (JS, C # и т. Д.) Используют неизменяемые строки, что разрушает всю идею перемещения строки без выделения новой памяти.

Хотя приведенные выше решения действительно обращают строку, они не делают это, не выделяя больше памяти, и, следовательно, не удовлетворяют условиям. Вы должны иметь прямой доступ к выделенной строке и иметь возможность манипулировать ее исходным местоположением в памяти, чтобы иметь возможность обратить ее на место.

Лично я действительно ненавижу такие вопросы на собеседовании, но, к сожалению, я уверен, что мы будем видеть их в течение многих лет.


7
Я могу, по крайней мере, сказать, что некоторое время назад у меня был один интервьюер, и он был очень впечатлен, когда он спросил меня, как изменить строку «на месте» в JS, и я объяснил, почему это невозможно, поскольку строки в JS являются неизменяемыми. Я не знаю, был ли это ответ, который он ожидал, или я немного его обучил. В любом случае все получилось хорошо;)
Chev

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('') не работает. Смотрите этот ответ для более подробного объяснения.
Михал Перлаковский

5
Это должен быть принятый ответ, потому что он также работает с Unicode. Например, из приведенного выше примера:Array.from('foo 𝌆 bar mañana mañana').reverse().join('') == 'anãnam anañam rab 𝌆 oof'
Джулиан Т.Ф.

3
@JulianTF Не совсем, одна тильда по-прежнему применяется к «а» вместо «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('') метод split, с помощью оператора распространения, например, так:

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

1
ES6 также позволяет использовать два обратных ('')

в этом случае нет причин использовать два обратных кавычка
Вик

1
Если вы не играете в гольф, вам следует избегать этого. Написание string.split('')понятнее для большинства людей, чем [...string].
AnnanFay

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

@ Inkling Я не понимал, что это проблема. Спасибо за указание на это. Я все еще хотел бы написать функцию полезности для ясности.
AnnanFay

19

Кажется, я на три года опоздал на вечеринку ...

К сожалению, вы не можете, как было указано. Видеть Являются ли строки JavaScript неизменяемыми? Нужен ли мне «строитель строк» ​​в JavaScript?

Следующая лучшая вещь, которую вы можете сделать, это создать «представление» или «оболочку», которая принимает строку и переопределяет любые части используемого вами строкового 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
Интересное использование рекурсии. Странно, что это переполнение стека. 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 loop

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
Хорошо, что вы включили эмодзи в свой пример. Так что мы быстро видим, что это явно не работает для смайликов и многих других символов Юникода.
Хор Ме

Вера, хотя ваш ответ правильный, я не согласен с тем, что это самый простой способ. Первые несколько ответов использовать 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('');
}

Все подробности о Матиасе, 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 reverse(str) {
  // Use the split() method to return a new array
  //  Use the reverse() method to reverse the new created array
  // Use the join() method to join all elements of the array into a string
  return str.split("").reverse().join("");
}
console.log(reverse('hello'));


Перевернуть строку без помощников

function reversedOf(str) {
  let newStr = '';
  for (let char of str) {
    newStr = char + newStr
    // 1st round: "h" + "" = h, 2nd round: "e" + "h" = "eh" ... etc. 
    // console.log(newStr);
  }
  return newStr;
}
console.log(reversedOf('hello'));


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 должен начинаться с 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

Используя функции Array,

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);

Как это "на месте" ??
Судхансу Чоудхари

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"
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.