Substituir Última Ocorrência em String

Beginner

This tutorial is from open-source community. Access the source code

Introdução

Neste laboratório, exploraremos a manipulação de strings em JavaScript implementando uma função replaceLast. Diferente do método replace embutido, que substitui a primeira ocorrência de um padrão, nossa função terá como alvo especificamente a última ocorrência. Essa capacidade é útil em muitos cenários do mundo real, como atualizar extensões de arquivos, modificar URLs ou limpar texto.

Ao longo deste laboratório, você aprenderá sobre métodos de string, expressões regulares e como combiná-los para criar uma função utilitária útil. Ao final deste laboratório, você terá uma sólida compreensão das técnicas de manipulação de strings em JavaScript e uma função reutilizável para seus projetos de codificação.

Compreendendo o Problema e Configurando

Antes de começarmos a codificar, vamos entender o que nossa função replaceLast deve fazer:

  1. Aceitar três parâmetros:

    • str: A string de entrada a ser modificada
    • pattern: A substring ou expressão regular a ser pesquisada
    • replacement: A string para substituir a última ocorrência
  2. Retornar uma nova string com a última ocorrência do padrão substituída.

Vamos criar um arquivo JavaScript para implementar nossa função:

  1. Navegue até o diretório do projeto no explorador de arquivos do WebIDE.
  2. Crie um novo arquivo chamado replaceLast.js no diretório replace-last.
  3. Adicione a seguinte estrutura básica ao arquivo:
// Function to replace the last occurrence of a pattern in a string
function replaceLast(str, pattern, replacement) {
  // Our implementation will go here
  return str;
}

// We will add test cases here later

Para verificar se tudo está configurado corretamente, vamos adicionar um teste simples:

// Example usage
console.log(replaceLast("Hello world world", "world", "JavaScript"));

Agora, vamos executar nosso código para ver a saída atual:

  1. Abra o Terminal no WebIDE
  2. Navegue até o diretório replace-last:
    cd ~/project/replace-last
  3. Execute o arquivo JavaScript usando Node.js:
    node replaceLast.js

Você deve ver Hello world world na saída porque nossa função atualmente apenas retorna a string original sem fazer nenhuma alteração.

Implementando a Lógica da Função Principal

Agora que entendemos o problema, vamos implementar a funcionalidade principal da nossa função replaceLast. Vamos nos concentrar em lidar com padrões de string primeiro, e depois abordaremos expressões regulares na próxima etapa.

Quando o padrão é uma string, podemos usar o método lastIndexOf para encontrar a posição da última ocorrência. Uma vez que sabemos essa posição, podemos usar o método slice para reconstruir a string com a substituição inserida.

Atualize sua função replaceLast com a seguinte implementação:

function replaceLast(str, pattern, replacement) {
  // Ensure inputs are valid
  if (typeof str !== "string") {
    return str;
  }

  if (typeof pattern === "string") {
    // Find the position of the last occurrence
    const lastIndex = str.lastIndexOf(pattern);

    // If pattern not found, return original string
    if (lastIndex === -1) {
      return str;
    }

    // Rebuild the string with the replacement
    const before = str.slice(0, lastIndex);
    const after = str.slice(lastIndex + pattern.length);
    return before + replacement + after;
  }

  // We'll handle regex patterns in the next step
  return str;
}

Atualize seus casos de teste para verificar se a função lida corretamente com padrões de string:

// Test cases for string patterns
console.log(replaceLast("Hello world world", "world", "JavaScript")); // Should output: "Hello world JavaScript"
console.log(replaceLast("abcabcabc", "abc", "123")); // Should output: "abcabc123"
console.log(replaceLast("abcdef", "xyz", "123")); // Should output: "abcdef" (pattern not found)

Execute o código novamente para ver a saída atualizada:

node replaceLast.js

Você deve agora ver a última ocorrência do padrão de string substituída em cada caso de teste. Por exemplo, "Hello world JavaScript" em vez de "Hello world world".

Lidando com Padrões de Expressão Regular

Agora, vamos aprimorar nossa função para lidar com padrões de expressão regular. Quando o padrão é uma expressão regular, precisamos:

  1. Encontrar todas as correspondências na string
  2. Obter a última correspondência
  3. Substituir essa última correspondência pela string de substituição

Atualize sua função replaceLast para lidar com padrões de expressão regular:

function replaceLast(str, pattern, replacement) {
  // Ensure inputs are valid
  if (typeof str !== "string") {
    return str;
  }

  // Handle string patterns
  if (typeof pattern === "string") {
    const lastIndex = str.lastIndexOf(pattern);
    if (lastIndex === -1) {
      return str;
    }
    const before = str.slice(0, lastIndex);
    const after = str.slice(lastIndex + pattern.length);
    return before + replacement + after;
  }

  // Handle regular expression patterns
  if (pattern instanceof RegExp) {
    // Create a new RegExp with global flag to find all matches
    const globalRegex = new RegExp(pattern.source, "g");

    // Find all matches
    const matches = str.match(globalRegex);

    // If no matches, return original string
    if (!matches || matches.length === 0) {
      return str;
    }

    // Get the last match
    const lastMatch = matches[matches.length - 1];

    // Find the position of the last match
    const lastIndex = str.lastIndexOf(lastMatch);

    // Rebuild the string with the replacement
    const before = str.slice(0, lastIndex);
    const after = str.slice(lastIndex + lastMatch.length);
    return before + replacement + after;
  }

  // If pattern is neither string nor RegExp, return original string
  return str;
}

Adicione casos de teste para padrões de expressão regular:

// Test cases for string patterns
console.log(replaceLast("Hello world world", "world", "JavaScript")); // Should output: "Hello world JavaScript"
console.log(replaceLast("abcabcabc", "abc", "123")); // Should output: "abcabc123"
console.log(replaceLast("abcdef", "xyz", "123")); // Should output: "abcdef" (pattern not found)

// Test cases for regular expression patterns
console.log(replaceLast("Hello world world", /world/, "JavaScript")); // Should output: "Hello world JavaScript"
console.log(replaceLast("123 456 789", /\d+/, "numbers")); // Should output: "123 456 numbers"
console.log(replaceLast("abcdef", /xyz/, "123")); // Should output: "abcdef" (pattern not found)

Execute o código novamente para ver a saída atualizada:

node replaceLast.js

Tanto os padrões de string quanto os padrões de expressão regular devem agora funcionar corretamente na função replaceLast.

Otimizando a Função e Testando Casos Extremos

Nossa função funciona para casos básicos, mas vamos otimizá-la e lidar com alguns casos extremos:

  1. Devemos verificar se a string de entrada está vazia
  2. Podemos simplificar o tratamento de expressões regulares
  3. Devemos lidar com casos em que a substituição não é uma string

Atualize sua função replaceLast com essas otimizações:

function replaceLast(str, pattern, replacement) {
  // Ensure str is a string
  if (typeof str !== "string") {
    return str;
  }

  // If str is empty or pattern is not provided, return original string
  if (str === "" || pattern === undefined) {
    return str;
  }

  // Ensure replacement is a string
  if (replacement === undefined) {
    replacement = "";
  } else if (typeof replacement !== "string") {
    replacement = String(replacement);
  }

  // Handle string patterns
  if (typeof pattern === "string") {
    const lastIndex = str.lastIndexOf(pattern);
    if (lastIndex === -1) {
      return str;
    }
    return (
      str.slice(0, lastIndex) +
      replacement +
      str.slice(lastIndex + pattern.length)
    );
  }

  // Handle regular expression patterns
  if (pattern instanceof RegExp) {
    // Create a global version of the regex to find all matches
    const globalRegex = new RegExp(
      pattern.source,
      "g" + (pattern.ignoreCase ? "i" : "") + (pattern.multiline ? "m" : "")
    );

    // Find all matches
    const matches = str.match(globalRegex);

    // If no matches, return original string
    if (!matches || matches.length === 0) {
      return str;
    }

    // Get the last match
    const lastMatch = matches[matches.length - 1];

    // Find the position of the last match
    const lastIndex = str.lastIndexOf(lastMatch);

    // Rebuild the string with the replacement
    return (
      str.slice(0, lastIndex) +
      replacement +
      str.slice(lastIndex + lastMatch.length)
    );
  }

  // If pattern is neither string nor RegExp, return original string
  return str;
}

Vamos adicionar mais casos de teste para cobrir os casos extremos:

// Test cases for string patterns
console.log(replaceLast("Hello world world", "world", "JavaScript")); // Should output: "Hello world JavaScript"
console.log(replaceLast("abcabcabc", "abc", "123")); // Should output: "abcabc123"
console.log(replaceLast("abcdef", "xyz", "123")); // Should output: "abcdef" (pattern not found)

// Test cases for regular expression patterns
console.log(replaceLast("Hello world world", /world/, "JavaScript")); // Should output: "Hello world JavaScript"
console.log(replaceLast("123 456 789", /\d+/, "numbers")); // Should output: "123 456 numbers"
console.log(replaceLast("abcdef", /xyz/, "123")); // Should output: "abcdef" (pattern not found)

// Edge cases
console.log(replaceLast("", "abc", "123")); // Should output: "" (empty string)
console.log(replaceLast("abcdef", "", "123")); // Should output: "abcde123f" (empty pattern)
console.log(replaceLast("abcdef", "def", "")); // Should output: "abc" (empty replacement)
console.log(replaceLast("AbCdEf", /[a-z]/, "X")); // Should output: "AbCdEX" (case-sensitive regex)
console.log(replaceLast("AbCdEf", /[a-z]/i, "X")); // Should output: "AbCdEX" (case-insensitive regex)

Execute o código novamente para ver a saída atualizada:

node replaceLast.js

Esta versão da função lida com mais casos extremos e mantém o código limpo. Agora você tem uma função replaceLast robusta, pronta para usar em seus projetos.

Criando um Módulo e Usando a Função

Nesta etapa final, converteremos nossa função em um módulo JavaScript adequado que pode ser importado e usado em outros arquivos. Esta é uma prática comum no desenvolvimento JavaScript do mundo real.

Primeiro, vamos criar um arquivo de módulo para nossa função. Crie um novo arquivo chamado replaceLastModule.js no diretório replace-last:

/**
 * Replaces the last occurrence of a pattern in a string.
 *
 * @param {string} str - The input string.
 * @param {string|RegExp} pattern - The pattern to replace (string or RegExp).
 * @param {string} replacement - The replacement string.
 * @returns {string} - The string with the last occurrence replaced.
 */
function replaceLast(str, pattern, replacement) {
  // Ensure str is a string
  if (typeof str !== "string") {
    return str;
  }

  // If str is empty or pattern is not provided, return original string
  if (str === "" || pattern === undefined) {
    return str;
  }

  // Ensure replacement is a string
  if (replacement === undefined) {
    replacement = "";
  } else if (typeof replacement !== "string") {
    replacement = String(replacement);
  }

  // Handle string patterns
  if (typeof pattern === "string") {
    const lastIndex = str.lastIndexOf(pattern);
    if (lastIndex === -1) {
      return str;
    }
    return (
      str.slice(0, lastIndex) +
      replacement +
      str.slice(lastIndex + pattern.length)
    );
  }

  // Handle regular expression patterns
  if (pattern instanceof RegExp) {
    // Create a global version of the regex to find all matches
    const globalRegex = new RegExp(
      pattern.source,
      "g" + (pattern.ignoreCase ? "i" : "") + (pattern.multiline ? "m" : "")
    );

    // Find all matches
    const matches = str.match(globalRegex);

    // If no matches, return original string
    if (!matches || matches.length === 0) {
      return str;
    }

    // Get the last match
    const lastMatch = matches[matches.length - 1];

    // Find the position of the last match
    const lastIndex = str.lastIndexOf(lastMatch);

    // Rebuild the string with the replacement
    return (
      str.slice(0, lastIndex) +
      replacement +
      str.slice(lastIndex + lastMatch.length)
    );
  }

  // If pattern is neither string nor RegExp, return original string
  return str;
}

// Export the function
module.exports = replaceLast;

Agora, vamos criar outro arquivo para usar nosso módulo. Crie um novo arquivo chamado app.js no diretório replace-last:

// Import the replaceLast function
const replaceLast = require("./replaceLastModule");

// Examples of using the replaceLast function
console.log(
  "Example 1:",
  replaceLast("Hello world world", "world", "JavaScript")
);
console.log("Example 2:", replaceLast("abcabcabc", "abc", "123"));
console.log("Example 3:", replaceLast("file.txt.backup.txt", ".txt", ".md"));
console.log("Example 4:", replaceLast("123 456 789", /\d+/, "numbers"));
console.log(
  "Example 5:",
  replaceLast("The fox jumped over the lazy dog", /[a-z]+/i, "cat")
);

// Practical examples
const filePath = "/path/to/my/file.txt";
console.log("File with new extension:", replaceLast(filePath, ".txt", ".md"));

const url = "https://example.com/products/category/item?color=red";
console.log("URL with updated parameter:", replaceLast(url, "red", "blue"));

const htmlTag = "<div class='container'><p>Text</p></div>";
console.log(
  "HTML with replaced tag:",
  replaceLast(htmlTag, /<\/?\w+>/g, "<span>")
);

Execute o aplicativo para ver como o módulo funciona:

node app.js

Você deve ver a saída com todos os exemplos demonstrando como a função replaceLast pode ser usada em vários cenários.

Parabéns. Você criou com sucesso uma função utilitária JavaScript útil e a empacotou como um módulo que pode ser reutilizado em seus projetos.

Resumo

Neste laboratório, você aprendeu como criar uma função utilitária JavaScript que substitui a última ocorrência de um padrão em uma string. Ao longo do processo, você adquiriu conhecimento valioso sobre:

  • Métodos de manipulação de strings como lastIndexOf e slice
  • Trabalhar com expressões regulares em JavaScript
  • Lidar com diferentes tipos de entradas e casos extremos
  • Criar e exportar módulos JavaScript
  • Escrever código limpo e reutilizável com documentação adequada

A função replaceLast que você construiu é uma utilidade prática que pode ser usada em muitos cenários do mundo real, como manipulação de caminhos de arquivos, modificações de URLs e processamento de texto. Este tipo de manipulação de strings é comumente necessário, mas não está diretamente disponível na biblioteca padrão do JavaScript.

Ao entender os conceitos e técnicas abordados neste laboratório, você está agora mais bem equipado para resolver problemas semelhantes de manipulação de strings em seus futuros projetos de programação.