جافا سكريبت - كيف تقوم بعكس سلسلة نصية في مكانها؟


كيف تقوم بعكس سلسلة نصية في المكان في JavaScript عندما يتم تمريرها إلى دالة مع عبارة return، بدون استخدام الدوال المدمجة (.reverse()، .charAt() إلخ)؟

الإجابة رقم 1

طالما أنك تتعامل مع حروف ASCII بسيطة، وسعيد باستخدام الدوال المدمجة، فإن هذا سيعمل:

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

إذا كنت بحاجة إلى حل يدعم UTF-16 أو الأحرف متعددة البايتات الأخرى، فكن على علم بأن هذه الدالة ستعطي سلاسل يونيكود غير صالحة، أو سلاسل صالحة تبدو غريبة. قد ترغب في النظر في هذا الجواب بدلاً من ذلك.

عامل توسيع المصفوفة مدرك لليونيكود:

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

حل آخر يدرك اليونيكود باستخدام split()، كما هو موضح على MDN، هو استخدام تعبير نمطي مع ضبط العلم u (اليونيكود) كمحدد.

function reverse(s){
    return s.split(/(?:)/u).reverse().join("");
}

الإجابة رقم 2

التقنية التالية (أو ما شابهها) تُستخدم عادة لعكس سلسلة في جافاسكريبت:

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

في الواقع، جميع الإجابات التي نُشرت حتى الآن هي مجرد نسخة من هذا النمط. ومع ذلك، هناك بعض المشاكل مع هذا الحل. على سبيل المثال:

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

إذا كنت تتساءل لماذا يحدث هذا، فاقرأ عن ترميز الأحرف الداخلي في جافاسكريبت. (لمحة سريعة: 𝌆 هو رمز فلكي، وجافاسكريبت تعرضه كوحدتين شيفرتين منفصلتين.)

لكن هناك المزيد:

// 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 الحرف اللاتيني الصغير N) وعلامة الدمج (U+0303 التي تجمع التلدة) ككل. سيؤدي عكس ترتيبها إلى ربط علامة الدمج بحرف مختلف في السلسلة. لهذا السبب، كانت نتيجة المثال بدلاً من ñ.

نأمل أن يوضح هذا سبب كون جميع الإجابات المنشورة حتى الآن خاطئة.

للإجابة على سؤالك الأول — كيفية عكس سلسلة نصية بشكل [صحيح] في JavaScript —، لقد كتبت مكتبة JavaScript صغيرة قادرة على عكس السلاسل النصية مع مراعاة Unicode. لا تحتوي على أي من المشاكل التي ذكرتها للتو. تُسمى المكتبة Esrever؛ شفرتها متاحة على GitHub، وتعمل في أي بيئة JavaScript تقريبًا. تأتي مع أداة/برنامج تنفيذي على سطر الأوامر، بحيث يمكنك بسهولة عكس السلاسل النصية من الطرفية إذا رغبت في ذلك.

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

أما بالنسبة لجزء "في المكان"، فراجع الإجابات الأخرى.

الإجابة رقم 3

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

الإجابة رقم 4

تحليل مفصل وعشر طرق مختلفة لعكس سلسلة ونقاط الأداء الخاصة بها.

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

أداء هذه التطبيقات:

أفضل تنفيذ/تنفيذه أداءً لكل متصفح

  • كروم 15 - التنفيذات 1 و 6
  • فايرفوكس 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));
}

التنفيذ 11

var reverser  = function(str){
let string = str.split('');

    for(i=0;i<string.length;i++){
        debugger;
        string.splice(i,0,string.pop());
    
    } 
    console.log(string.join())
}
reverser('abcdef')

الإجابة رقم 5

كل فكرة "عكس سلسلة النصوص في مكانها" هي سؤال قديم للمقابلات يُطرحه مبرمجو C، والأشخاص الذين تمت مقابلتهم من قبلهم (ربما للانتقام؟). للأسف، الجزء المتعلق بـ "في المكان" لم يعد يعمل لأن السلاسل النصية في أي لغة مُدارة تقريبًا (JS، C#، إلخ) تستخدم سلاسل نصية ثابتة، مما يُفشل الفكرة كاملة حول نقل سلسلة النصوص دون تخصيص ذاكرة جديدة.

بينما تعمل الحلول أعلاه بالفعل على عكس السلسلة، إلا أنها لا تفعل ذلك دون تخصيص المزيد من الذاكرة، وبالتالي فهي لا تلبي الشروط. تحتاج إلى الوصول المباشر إلى السلسلة كما هي مخصصة، وأن تكون قادراً على التعامل مع موقع الذاكرة الأصلي الخاص بها لتتمكن من عكسها في مكانها.

شخصيًا، أنا حقًا أكره هذا النوع من أسئلة المقابلات، ولكن للأسف، أنا متأكد أننا سنستمر في رؤيتها لسنوات قادمة.

الإجابة رقم 6

أولاً، استخدم Array.from() لتحويل سلسلة إلى مصفوفة، ثم Array.prototype.reverse() لعكس المصفوفة، ثم Array.prototype.join() لإعادتها كسلسلة.

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

الإجابة رقم 7

في ECMAScript 6، يمكنك عكس سلسلة بسرعة أكبر دون استخدام طريقة .split('')، باستخدام عامل الانتشار كما يلي:

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

الإجابة رقم 8

يبدو أنني متأخر عن الحفل بثلاث سنوات...

للأسف لا يمكنك ذلك كما تم الإشارة إليه. انظر هل سلاسل جافا سكريبت غير قابلة للتغيير؟ هل أحتاج إلى "منشئ سلاسل" في جافا سكريبت؟

أفضل شيء يمكنك فعله بعد ذلك هو إنشاء "عرض" أو "غلاف"، والذي يأخذ سلسلة ويعيد تنفيذ أي أجزاء من واجهة برمجة تطبيقات السلسلة التي تستخدمها، ولكن مع التظاهر بأن السلسلة معكوسة. على سبيل المثال:

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"

هذا يحقق توفيراً كبيراً إذا تم تطبيقه على سلسلة كبيرة جداً، إذا كنت تأخذ جزءاً صغيراً نسبياً منها فقط.

ما إذا كان هذا يستحق ذلك (مقابل العكس كنسخة كما هو الحال في معظم لغات البرمجة) يعتمد بشكل كبير على حالة الاستخدام الخاصة بك ومدى كفاءة إعادة تنفيذ واجهة برمجة التطبيقات للسلسلة. على سبيل المثال، إذا كان كل ما تريده هو التلاعب بمؤشرات السلسلة، أو أخذ شرائح صغيرة أو جزء من السلسلة صغير، فإن هذا سيوفر لك مساحة ووقت. ومع ذلك، إذا كنت تخطط لطباعة شرائح أو أجزاء معكوسة كبيرة، فقد تكون التوفير ضئيلاً حقًا، وربما أسوأ من القيام بنسخ كامل. أيضًا، السلسلة "المعكوسة" الخاصة بك لن تكون من النوع string، رغم أنه قد يكون بإمكانك التظاهر بذلك باستخدام النمذجة الأولية.

يُنشئ تنفيذ العرض التوضيحي أعلاه كائنًا جديدًا من نوع ReversedString. إنه مهيأ بواسطة النموذج الأولي، ولذلك فهو فعال إلى حد ما، مع عمل شبه ضئيل وحمل مساحي منخفض جدًا (تعريفات النموذج الأولي مشتركة). إنه تنفيذ كسول يتضمن القطع المؤجل. في كل مرة تقوم فيها بتنفيذ وظيفة مثل .slice أو .reversed، فإنه سيقوم بإجراء حسابات الفهرس. وأخيرًا، عندما تستخرج البيانات (عن طريق استدعاء .toString() أو .charCodeAt(...) أو شيء مشابه ضمنيًا)، سيطبق تلك العمليات بطريقة "ذكية"، لمس أقل قدر ممكن من البيانات.

ملاحظة: واجهة برمجة التطبيقات للسلاسل أعلاه هي مثال، وقد لا تكون مطبقة بشكل كامل. يمكنك أيضًا استخدام دالتين فقط أو دالة واحدة حسب حاجتك.

الإجابة رقم 9

طريقة قابلة للقراءة باستخدام صيغة الانتشار:

const reverseString = str => [...str].reverse().join('');

console.log(reverseString('ABC'));

الإجابة رقم 10

هناك العديد من الطرق التي يمكنك بها عكس نص في جافاسكريبت. أنا أدون هنا ثلاث طرق أفضلها.

النهج 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: استخدام دالة reduce:

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

آمل أن هذا يساعد :)

الإجابة رقم 11

هناك عدة طرق للقيام بذلك، يمكنك مراجعة ما يلي،

1. الحلقة التقليدية for (الزيادة):

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

2. حلقة for التقليدية (تناقصية):

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:

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

الإجابة رقم 12

أثناء مقابلة، طُلب مني عكس سلسلة دون استخدام أي متغيرات أو طرق أصلية. هذا هو تنفيذي المفضل:

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