【问题标题】:Return String as Sorted Blocks - Codewars Challenge - JavaScript将字符串作为排序块返回 - Codewars 挑战 - JavaScript
【发布时间】:2020-01-08 02:14:12
【问题描述】:

我一直在尝试解决this codewars challenge。这个想法是返回字符串,根据其层次结构重新排列,或者根据重复字符分成块。


您将收到一个由小写字母、大写字母和数字组成的字符串作为输入。您的任务是将此字符串作为由破折号(“-”)分隔的块返回。块的元素应按照下面列出的层次结构进行排序,并且每个块不能包含同一字符的多个实例。

层次结构是:

小写字母 (a - z),按字母顺序排列 大写字母 (A - Z),按字母顺序 数字 (0 - 9),按升序排列 例子

"21AxBz" -> "xzAB12"

  • 由于输入不包含重复字符,因此您只需要 1 个块

"abacad" -> "abcd-a-a"

  • 字符“a”重复 3 次,因此需要 3 个块

"" -> ""

  • 空输入应导致空输出

我尝试过的实际上适用于给定的测试用例:

describe("Sample tests", () => {
  it("Tests", () => {
    assert.equal(blocks("21AxBz"), "xzAB12");
    assert.equal(blocks("abacad"), "abcd-a-a");
    assert.equal(blocks(""), "");
  });
});

但是当有任何重复字符时会失败,除了在测试用例中:

function repeatingChar(str){
  const result = [];
  const strArr = str.toLowerCase().split("").sort().join("").match(/(.)\1+/g);
  if (strArr != null) {
    strArr.forEach((elem) => {
      result.push(elem[0]);
    });
  }
  return result;
}

function blocks(s) {
  if (s.length === 0) {
    return '';
  }
  //if string does not contain repeating characters
  if (!/(.).*\1/.test(s) === true) {
    let lower = s.match(/[a-z]/g).join('');
    let upper = s.match(/[A-Z]/g).join('');
    let numbers = s.match(/[\d]/g).sort().join('');
    return lower + upper + numbers;
  }
  //if string contains repeating characters
  if (!/(.).*\1/.test(s) === false) {
    let repeatChar = (repeatingChar(s)[0]);
    let repeatRegex = new RegExp(repeatingChar(s)[0]);
    let repeatCount = s.match(/[repeatRegex]/gi).length;
    let nonAChars = s.match(/[^a]/gi).join('');
    function getPosition(string, subString, index) {
      return s.split(repeatChar, index).join(repeatChar).length;
    }
    let index = getPosition(s, repeatChar, 2);
    // console.log('indexxxxx', index);
    return s.slice(0, index) + nonAChars.slice(1) + ('-' + repeatChar).repeat(repeatCount - 1);
  }
}
console.log(blocks("abacad"));

实际上,我不确定它有什么问题,因为我不知道如何解锁 Codewars 上的任何其他测试。

您可以看到我正在尝试做的是找到重复字符,获取所有不是重复字符的字符,然后将字符串从起点切到重复字符的 2 个实例,然后在末尾添加剩余的重复字符,用破折号分隔。

关于如何做到这一点的任何其他建议?

【问题讨论】:

    标签: javascript regex string substring


    【解决方案1】:

    对于有趣的人,这是我解决问题的方法:

    const isLower = new RegExp('[a-z]');
    const isUpper = new RegExp('[A-Z]'); 
    const isDigit = new RegExp('[0-9]');
    const isDigitOrUpper = new RegExp('[0-9A-Z]');
    const isDigitOrLower = new RegExp('[0-9a-z]');
    const isLowerOrUpper = new RegExp('[a-zA-Z]');
    
    function lowerUpperNumber(a, b)
    {
      if(isLower.test(a) && isDigitOrUpper.test(b))
      {
        return -1;
      }
      else if(isUpper.test(a) && isDigitOrLower.test(b))
      {
        if(isDigit.test(b))
        {
          return -1;
        }
        else if(isLower.test(b))
        {
          return 1;
        }
      }
      else if(isDigit.test(a) && isLowerOrUpper.test(b))
      {
        return 1;
      }
      else if(a > b)
      {
        return 1;
      }
      else if(a < b)
      {
        return -1;
      }
      return 0;
    }
    
    function makeBlocks(input)
    {
      let sortedInput = input.split('');
      sortedInput.sort(lowerUpperNumber);
      let output = '';
      let blocks = [];
      for(let c of sortedInput)
      {
        let inserted = false;
        for(let block of blocks)
        {
          if(block.indexOf(c) === -1)
          {
            inserted = true;
            block.push(c);
            break;
          }
        }
        if(!inserted)
        {
          blocks.push([c]);
        }
      }
      
      output = blocks.map(block => block.join('')).join('-');
      
      return output;
    }
    
    console.log(makeBlocks('21AxBz'));
    console.log(makeBlocks('abacad'));
    console.log(makeBlocks('Any9Old4String22With7Numbers'));
    console.log(makeBlocks(''));

    【讨论】:

    • 我不知道为什么,但这在 Codewars blocks is not defined: i.imgur.com/5qtyMPX.png 上返回了一个引用错误,但块肯定被定义为 makeBlocks 中的空数组?
    • 在代码战挑战中?那是因为函数需要被称为“块”,所以只需将其从 makeBlocks 重命名为块就可以了。我对名称冲突不满意,但 javascript 不介意。
    【解决方案2】:

    我看到的第一个明显错误是let repeatCount = s.match(/[repeatRegex]/gi).length;。你真正想做的是:

      let repeatRegex = new RegExp(repeatingChar(s)[0], 'g');
      let repeatCount = s.match(repeatRegex).length;
    

    接下来是你只看一个重复的字符,而不是所有的,所以你不会得到正确形式的块,所以你需要循环它们。

    let repeatedChars = repeatingChar(s);
    for(let c of repeatedChars)
    {
      //figure out blocks
    }
    

    在构建模块时,您已决定专注于所有非“a”的事物。我猜这不是你最初写的,而是一些调试代码,用于处理那个示例输入。

    如果我正确理解你的愿望,你想把所有不重复的字符放在一起,然后把第一个重复字符的第一个实例放在前面,然后把剩余的重复字符塞进去背面字符,以-分隔。

    这里的问题是第一个重复字符可能不是结果中应该排在第一位的字符。本质上,您很幸运,因为重复字符是 a。

    修复您的代码,我将创建一个数组并单独构建块,然后在最后将它们全部连接在一起。

    let repeatedChars = repeatingChar(s);
    let blocks = []
    for(let c of repeatedChars)
    {
      let repeatRegex = new RegExp(c, 'g');
      let repeatCount = s.match(repeatRegex).length;
    
      for(let i = 1; i <= repeatCount; i++)
      {
        if(blocks.length < i)
        {
          let newBlock = [c];
          blocks.push(newBlock);
        }
        else
        {
          block[i - 1].push(c);
        }
      }
    }
    
    let tailBlocks = blocks.map(block => block.join('')).join('-');
    

    但是,这给我留下了一个问题,即如何以正确的顺序构建包含非重复字符的最终字符串。

    所以,首先,让我们创建初始字符串。为此,我们需要一个自定义排序函数(抱歉,它非常冗长。如果我们可以使用常规的 ASCII 排序):

    function lowerUpperNumber(a, b)
    {
      if(a.match(/[a-z]/) && b.match(/[A-Z0-9]/))
      {
          return -1; 
      } 
      else if(a.match(/[A-Z]/) && (b.match(/[0-9]/) || b.match(/[a-z]/)))
      {
        if(b.match(/[0-9]/))
        {
          return -1;
        } 
        else if(b.match(/[a-z]/))
        {
          return 1;
        }
      } 
      else if(a.match(/[0-9]/) && b.match(/[a-zA-Z]/))
      {
        return 1;
      }
      else if(a > b)
      {
        return 1;
      }
      else if(a < b)
      {
        return -1;
      }
      return 0;
    }
    

    然后创建最终输出的head:

    let firstBlock = [...(new Set(s))].sort(lowerUpperNumber);
    

    Set 创建了一组独特的元素,即没有重复。

    因为我们已经创建了头部字符串,所以在创建重复字符块时,我们需要比上述循环少一个,所以我们将使用s.match(repeatRegex).length-1。

    当没有重复字符时,我希望将复杂的位短路并快速返回,但为了简洁起见,我将删除该位,而且我不想处理未定义的值(例如尝试'123' 作为您的输入)。

    让我们把它们放在一起:

    function lowerUpperNumber(a, b)
    {
      if(a.match(/[a-z]/) && b.match(/[A-Z0-9]/))
      {
          return -1; 
      } 
      else if(a.match(/[A-Z]/) && (b.match(/[0-9]/) || b.match(/[a-z]/)))
      {
        if(b.match(/[0-9]/))
        {
          return -1;
        } 
        else if(b.match(/[a-z]/))
        {
          return 1;
        }
      } 
      else if(a.match(/[0-9]/) && b.match(/[a-zA-Z]/))
      {
        return 1;
      }
      else if(a > b)
      {
        return 1;
      }
      else if(a < b)
      {
        return -1;
      }
      return 0;
    }
    
    function makeBlocks(s) 
    {
      if (s.length === 0) 
      {
        return '';
      }
       
      let firstBlock = [...(new Set(s))].sort(lowerUpperNumber);
      let firstString = firstBlock.join('');
      let blocks = [];
      
      for(let c of firstString)
      {
        let repeatRegex = new RegExp(c, 'g');
        let repeatCount = s.match(repeatRegex).length - 1;
    
        for(let i = 1; i <= repeatCount; i++)
        {
          if(blocks.length < i)
          {
            let newBlock = [c];
            blocks.push(newBlock);
          }
          else
          {
            blocks[i - 1].push(c);
          }
        }
      }
      
      blocks.unshift(firstBlock); 
    
      return blocks.map(block => block.join('')).join('-');  
    }
    console.log(makeBlocks('21AxBz'));
    console.log(makeBlocks('abacad'));
    console.log(makeBlocks('Any9Old4String22With7Numbers'));
    console.log(makeBlocks(''));

    您会看到我没有费心生成重复的字符,因为我可以跳过不重复的字符。

    【讨论】:

      猜你喜欢
      • 2019-11-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-09-25
      • 1970-01-01
      • 1970-01-01
      • 2013-10-28
      • 1970-01-01
      相关资源
      最近更新 更多