มีวิธีการต่อสายสำหรับสตริงหรือไม่?


92

Javascript spliceใช้งานได้กับอาร์เรย์เท่านั้น มีวิธีการที่คล้ายกันสำหรับสตริงหรือไม่? หรือฉันควรสร้างฟังก์ชันที่กำหนดเอง

substr()และsubstring()วิธีเดียวที่จะกลับสตริงสกัดและไม่สามารถปรับเปลี่ยนสายเดิม สิ่งที่ฉันต้องการทำคือลบบางส่วนออกจากสตริงของฉันและใช้การเปลี่ยนแปลงกับสตริงเดิม ยิ่งไปกว่านั้นวิธีreplace()นี้จะใช้ไม่ได้ในกรณีของฉันเพราะฉันต้องการลบส่วนที่เริ่มต้นจากดัชนีและสิ้นสุดที่ดัชนีอื่น ๆ เหมือนกับที่ฉันสามารถทำได้ด้วยsplice()วิธีนี้ ฉันพยายามแปลงสตริงเป็นอาร์เรย์ แต่นี่ไม่ใช่วิธีที่เรียบร้อย


str.splitอาจเป็นฟังก์ชันที่คุณกำลังมองหา: google.com/#q=javascript+string+split
Anderson Green

1
เกิดอะไรขึ้นstr = str.slice()?
elclanrs

1
ดูเหมือนปัญหา XY คุณกำลังพยายามทำอะไรอยู่? สตริงจะไม่ถูกส่งผ่านโดยการอ้างอิงซึ่งแตกต่างจากอาร์เรย์และอ็อบเจ็กต์ดังนั้นคุณจะไม่กลายพันธุ์สตริงคุณสร้างสตริงใหม่หรือกำหนดสตริงใหม่ให้กับสตริงเก่า
elclanrs


6
return string.slice(0, startToSplice) + string.slice(endToSplice);, ไม่?
raina77ow

คำตอบ:


90

มันเร็วกว่าที่จะตัดสตริงสองครั้งเช่นนี้:

function spliceSlice(str, index, count, add) {
  // We cannot pass negative indexes directly to the 2nd slicing operation.
  if (index < 0) {
    index = str.length + index;
    if (index < 0) {
      index = 0;
    }
  }

  return str.slice(0, index) + (add || "") + str.slice(index + count);
}

มากกว่าการใช้การแบ่งตามด้วยการเข้าร่วม (วิธีการของ Kumar Harsh) เช่นนี้:

function spliceSplit(str, index, count, add) {
  var ar = str.split('');
  ar.splice(index, count, add);
  return ar.join('');
}

นี่คือjsperfที่เปรียบเทียบวิธีการทั้งสองและวิธีอื่น ๆ (jsperf ถูกลงมาหลายเดือนแล้วกรุณาแนะนำทางเลือกอื่นในความคิดเห็น)

แม้ว่าโค้ดด้านบนจะใช้ฟังก์ชันที่จำลองฟังก์ชันการทำงานทั่วไปspliceแต่การปรับโค้ดให้เหมาะสมสำหรับเคสที่ผู้ถามนำเสนอ (นั่นคือการไม่เพิ่มอะไรลงในสตริงที่แก้ไข) จะไม่ทำให้ประสิทธิภาพสัมพัทธ์ของวิธีการต่างๆเปลี่ยนไป


3
คุณสามารถเพิ่มสิ่งนี้ได้ทั่วโลกด้วย: String.prototype.splice = function (index, count, add) { return this.slice(0, index) + (add || "") + this.slice(index + count); } โปรดทราบว่ามันใช้งานไม่ได้เหมือนกับวิธีการต่ออาร์เรย์เนื่องจากสตริงไม่เปลี่ยนรูป (คุณไม่สามารถแก้ไขได้เมื่อสร้างแล้ว) การใช้งานจะเป็นดังนี้var s="ss"; s = s.splice(0,1,"t");
William Neely

@WilliamNeely: var StringUtils={'StringSplice':/*func def*/};จะเป็นมาตรฐานสำหรับการจัดการสตริงอย่างที่คุณบอกว่าไม่เปลี่ยนรูป
Mr.Polywhirl

spliceSlice และ spliceSplit จะไม่เท่ากับการทำงานเนื่องจาก Array.prototype.splice ยอมรับดัชนีลบ: jsfiddle.net/sykteho6/5
Martijn

1
@Mzialla ขอบคุณฉันได้เพิ่มรหัสเพื่อดูแลมัน
Louis

การสร้าง temp vars และการแนะนำสถานที่สำหรับข้อผิดพลาดแบบ off-by-one ไม่ต้องพูดถึงโค้ดที่ใช้เวลานานกว่าสำหรับมนุษย์ในการประมวลผลซึ่งต่างจากการแยก ("") splice (... ). join ("") เป็นการแลกเปลี่ยนที่ สมควรได้รับการพิจารณา ปัญหาความเร็วเป็นปัญหาหากเป็นปัญหาเท่านั้น
ballenf

16

แก้ไข

แน่นอนว่านี่ไม่ใช่วิธีที่ดีที่สุดในการ "ต่อสาย" สตริงฉันได้ให้สิ่งนี้เป็นตัวอย่างว่าการนำไปใช้งานเป็นอย่างไรซึ่งมีข้อบกพร่องและเห็นได้ชัดจากการแยก (), ประกบ () และเข้าร่วม () สำหรับการนำไปใช้งานที่ดีขึ้นโปรดดูวิธีการของ Louis


ไม่ไม่มีสิ่งที่เรียกว่า a String.spliceแต่คุณสามารถลองสิ่งนี้:

newStr = str.split(''); // or newStr = [...str];
newStr.splice(2,5);
newStr = newStr.join('');

ฉันรู้ว่าไม่มีspliceฟังก์ชันเหมือนใน Arrays ดังนั้นคุณต้องแปลงสตริงเป็นอาร์เรย์ โชคร้าย...


ในกรณีของฉันฉันกำลังทำงานกับสตริงแบบไดนามิกดังนั้นฉันต้องระบุดัชนีไม่ใช่ค่าอักขระ
ProllyGeek

ใช่นั่นเป็นเรื่องที่ถูกต้อง แต่คุณสามารถรับดัชนีของตัวละครได้โดยใช้ String.prototype.indexOf as"asdsd".indexOf('s');
kumarharsh

3
แทนที่จะใช้ str.split คุณสามารถใช้ไวยากรณ์การแพร่กระจาย ES6 ดังนี้:[...str]
robbie

5

นี่คือแกงเล็ก ๆ ที่ดีซึ่งให้ความสามารถในการอ่านได้ดีขึ้น (IMHO):

ลายเซ็นของฟังก์ชันที่สองจะเหมือนกับArray.prototype.spliceวิธีการ

function mutate(s) {
    return function splice() {
        var a = s.split('');
        Array.prototype.splice.apply(a, arguments);
        return a.join('');
    };
}

mutate('101')(1, 1, '1');

ฉันรู้ว่ามีคำตอบที่ยอมรับแล้ว แต่หวังว่านี่จะเป็นประโยชน์


1
Np. คุณยังสามารถสรุปสิ่งนี้เพื่ออนุญาตให้เป็น'method'พารามิเตอร์ที่เป็นทางเลือกเพื่อให้คุณสามารถส่งผ่านArray.prototype[method]และปฏิบัติกับสตริงได้อย่างสมบูรณ์เช่นอาร์เรย์ นอกจากนี้ด้วยสิ่งนี้เพียงอย่างเดียวคุณสามารถสรุปกระบวนการจริงไปยังฟังก์ชันอื่นได้และหากไม่มีสตริงให้ส่งคืนฟังก์ชันที่จะดึงสตริงและย้อนกลับแกง: mutate()(1, 1, '1')('101'); คุณยังสามารถ duckType อาร์กิวเมนต์เพื่อกำจัดแกงของการวิงวอนที่ว่างเปล่า - แม้กระทั่งห่อหุ้มเมธอดด้วยตัวมันเอง หากคุณต้องการสิ่งนี้คุณอาจต้องการที่จะมองเข้าไปในรูปแบบคำสั่งว่า เพียงแค่ถ่มน้ำลายที่นี่
Cody

3

ฉันต้องการเสนอทางเลือกที่ง่ายกว่าสำหรับทั้ง Kumar / Cody และวิธีการของ Louis ในการทดสอบทั้งหมดที่ฉันวิ่งมันทำงานได้เร็วพอ ๆ กับวิธีการของ Louis (ดูการทดสอบซอสำหรับเกณฑ์มาตรฐาน)

String.prototype.splice = function(startIndex,length,insertString){
    return this.substring(0,startIndex) + insertString + this.substring(startIndex + length);
};

คุณสามารถใช้งานได้ดังนี้:

var creditCardNumber = "5500000000000004";
var cardSuffix = creditCardNumber.splice(0,12,'****');
console.log(cardSuffix);  // output: ****0004

ดูผลการทดสอบ: https://jsfiddle.net/0quz9q9m/5/


อย่างน้อยคุณควรทำ "+ (insertString ||" ") +" แทน "+ insertString + , otherwise the third argument is not optional. This implementation also doesn't take care of startIndex <0` ซึ่งแตกต่างจากคำแนะนำอื่น ๆ ตัวอย่างการใช้งานที่ยอดเยี่ยมแม้ว่า
YakovL

3

ดูเหมือนจะมีความสับสนมากมายซึ่งได้รับการกล่าวถึงเฉพาะในความคิดเห็นโดย elclanrs และ raina77ow ดังนั้นให้ฉันโพสต์คำตอบที่ชัดเจน

ชี้แจง

จาก " string.splice" เราอาจคาดหวังได้เช่นเดียวกับอาร์เรย์:

  • ยอมรับได้ถึง 3 อาร์กิวเมนต์: ตำแหน่งเริ่มต้นความยาวและ (เป็นทางเลือก) การแทรก (สตริง)
  • ส่งคืนส่วนที่ตัดออก
  • แก้ไขสตริงเดิม

ปัญหาคือไม่สามารถตอบสนองความต้องการ 3d ได้เนื่องจากสตริงไม่เปลี่ยนรูป (เกี่ยวข้อง: 1 , 2 ) ฉันพบความคิดเห็นที่ทุ่มเทที่สุดที่นี่ :

ในสตริง JavaScript เป็นประเภทค่าดั้งเดิมไม่ใช่วัตถุ ( ข้อมูลจำเพาะ ) ในความเป็นจริงของ ES5 พวกเขาเป็นหนึ่งในเพียง 5 ประเภทค่าข้างnull, undefined, และnumber booleanStrings ได้รับมอบหมายจากค่าและไม่ได้โดยการอ้างอิงและจะถูกส่งผ่านดังกล่าว ดังนั้นสตริงไม่ได้เปลี่ยนรูปเพียงแค่พวกเขามีค่า การเปลี่ยนสาย"hello"ที่จะ"world"เป็นเหมือนการตัดสินใจว่าจากนี้ไปหมายเลข 3 เป็นจำนวน 4 ... มันทำให้รู้สึกไม่

ดังนั้นในบัญชีนี้เราอาจคาดหวังstring.spliceสิ่งที่ "" เท่านั้น:

  • ยอมรับได้ถึง 2 อาร์กิวเมนต์: ตำแหน่งเริ่มต้น, ความยาว (การแทรกไม่สมเหตุสมผลเนื่องจากไม่มีการเปลี่ยนแปลงสตริง)
  • ส่งคืนส่วนที่ตัดออก

ซึ่งคือสิ่งที่substrทำ; หรืออีกทางหนึ่งคือ

  • ยอมรับได้ถึง 3 อาร์กิวเมนต์: ตำแหน่งเริ่มต้นความยาวและ (เป็นทางเลือก) การแทรก (สตริง)
  • ส่งคืนสตริงที่แก้ไข (โดยไม่มีส่วนตัดและมีการแทรก)

ซึ่งเป็นเรื่องของส่วนถัดไป

แนวทางแก้ไข

หากคุณสนใจเกี่ยวกับการเพิ่มประสิทธิภาพคุณควรใช้การใช้งานของ Mike:

String.prototype.splice = function(index, count, add) {
    if (index < 0) {
        index += this.length;
        if (index < 0)
            index = 0;
    }
    return this.slice(0, index) + (add || "") + this.slice(index + count);
}

แม้ว่าการปฏิบัติต่อดัชนีนอกขอบเขตอาจแตกต่างกันไป ขึ้นอยู่กับความต้องการของคุณคุณอาจต้องการ:

    if (index < 0) {
        index += this.length;
        if (index < 0)
            index = 0;
    }
    if (index >= this.length) {
        index -= this.length;
        if (index >= this.length)
            index = this.length - 1;
    }

หรือแม้กระทั่ง

    index = index % this.length;
    if (index < 0)
        index = this.length + index;

หากคุณไม่สนใจเรื่องประสิทธิภาพคุณอาจต้องการปรับเปลี่ยนคำแนะนำของ Kumar ซึ่งตรงไปตรงมามากกว่า:

String.prototype.splice = function(index, count, add) {
    var chars = this.split('');
    chars.splice(index, count, add);
    return chars.join('');
}

ประสิทธิภาพ

ความแตกต่างในการแสดงจะเพิ่มขึ้นอย่างมากตามความยาวของสตริง jsperf แสดงให้เห็นว่าสำหรับสตริงที่มีความยาว 10 โซลูชันหลัง (การแยกและการรวม) จะช้ากว่าโซลูชันเดิมถึงสองเท่า (โดยใช้สไลซ์) สำหรับสตริง 100 ตัวอักษรคือ x5 และสำหรับสตริง 1,000 ตัวอักษรคือ x50 ใน Ops / วินาทีมัน:

                      10 letters   100 letters   1000 letters
slice implementation    1.25 M       2.00 M         1.91 M
split implementation    0.63 M       0.22 M         0.04 M

โปรดทราบว่าฉันได้เปลี่ยนอาร์กิวเมนต์ที่ 1 และ 2 เมื่อย้ายจาก 10 ตัวอักษรเป็น 100 ตัวอักษร (ฉันยังแปลกใจที่การทดสอบ 100 ตัวอักษรทำงานเร็วกว่า 10 ตัวอักษร)


2

เพียงใช้substrสำหรับสตริง

เช่น

var str = "Hello world!";
var res = str.substr(1, str.length);

ผลลัพธ์ = ello world!


1

ดังนั้นไม่ว่าจะเพิ่มวิธีการต่อสายให้กับต้นแบบ String ไม่สามารถทำงานได้ตามข้อกำหนด ...

ลองทำดูบ้าง:

String.prototype.splice = function(...a){
    for(var r = '', p = 0, i = 1; i < a.length; i+=3)
        r+= this.slice(p, p=a[i-1]) + (a[i+1]||'') + this.slice(p+a[i], p=a[i+2]||this.length);
    return r;
}
  • ทุก 3 args จัดกลุ่ม "การแทรก" ในลักษณะประกบกัน
  • พิเศษถ้ามีมากกว่าหนึ่งกลุ่ม 3 args การสิ้นสุดการตัดแต่ละครั้งจะเป็นจุดเริ่มต้นของถัดไป
    • '0123456789'.splice (4,1,' สี่ ', 8,1,' แปด '); // กลับ '0123fourth567eighth9'
  • คุณสามารถวางหรือทำให้อาร์กิวเมนต์สุดท้ายเป็นศูนย์ในแต่ละกลุ่มได้ (ซึ่งถือว่าเป็น "ไม่มีอะไรจะแทรก")
    • '0123456789'.splice (-4,2); // ส่งคืน '0123459'
  • คุณสามารถวางอาร์กิวเมนต์ทั้งหมดยกเว้นที่ 1 ในกลุ่มสุดท้าย (ซึ่งถือว่าเป็น "ตัดทั้งหมดหลังจากตำแหน่งอาร์กิวเมนต์แรก")
    • '0123456789'.splice (0,2, null, 3,1, null, 5,2,' / ', 8); // ส่งคืน '24 / 7 '
  • หากคุณผ่านหลายรายการคุณต้องตรวจสอบการเรียงลำดับจากซ้ายไปขวาด้วยตัวคุณเอง!
  • และถ้าคุณไม่ต้องการคุณต้องไม่ใช้มันเช่นนี้:
    • '0123456789'.splice (4, -3,' อะไร? '); // ส่งคืน "0123what? 123456789"

0

คำตอบของวิธีการของ Louisเป็นStringฟังก์ชันต้นแบบ:

String.prototype.splice = function(index, count, add) {
  if (index < 0) {
    index = this.length + index;
    if (index < 0) {
      index = 0;
    }
  }
  return this.slice(0, index) + (add || "") + this.slice(index + count);
}

ตัวอย่าง:

 > "Held!".splice(3,0,"lo Worl")
 < "Hello World!"

0

วิธีการspliceSlice ของ Louisล้มเหลวเมื่อเพิ่มค่าเป็น 0 หรือค่าเท็จอื่น ๆ นี่คือการแก้ไข:

function spliceSlice(str, index, count, add) {
  if (index < 0) {
    index = str.length + index;
    if (index < 0) {
      index = 0;
    }
  }
  const hasAdd = typeof add !== 'undefined';
  return str.slice(0, index) + (hasAdd ? add : '') + str.slice(index + count);
}

0

ฉันแก้ไขปัญหาของฉันโดยใช้รหัสนี้เป็นการทดแทนรอยต่อที่ขาดหายไป

let str = "I need to remove a character from this";
let pos = str.indexOf("character")

if(pos>-1){
  let result = str.slice(0, pos-2) + str.slice(pos, str.length);
  console.log(result) //I need to remove character from this 
}

ฉันต้องการลบอักขระก่อน / หลังคำบางคำหลังจากที่คุณได้รับตำแหน่งแล้วสตริงจะถูกแบ่งออกเป็นสองส่วนจากนั้นจึงเรียบเรียงใหม่โดยการลบอักขระอย่างยืดหยุ่นโดยใช้ออฟเซ็ตพร้อม pos

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.