简体   繁体   English

将字符串作为排序块返回 - Codewars 挑战 - JavaScript

[英]Return String as Sorted Blocks - Codewars Challenge - JavaScript

I've been trying to solve this codewars challenge .我一直在努力解决这个代码战挑战 The idea is to return the string, rearranged according to its hierarchy, or separated into chunks according to the repeating character.这个想法是返回字符串,根据其层次结构重新排列,或根据重复字符分成块。


You will receive a string consisting of lowercase letters, uppercase letters and digits as input.您将收到一个由小写字母、大写字母和数字组成的字符串作为输入。 Your task is to return this string as blocks separated by dashes ("-").您的任务是将此字符串作为由破折号(“-”)分隔的块返回。 The elements of a block should be sorted with respect to the hierarchy listed below, and each block cannot contain multiple instances of the same character.块的元素应按照下面列出的层次结构进行排序,并且每个块不能包含同一字符的多个实例。

The hierarchy is:层次结构是:

lowercase letters (a - z), in alphabetic order uppercase letters (A - Z), in alphabetic order digits (0 - 9), in ascending order Examples小写字母 (a - z),按字母顺序 大写字母 (A - Z),按字母顺序数字 (0 - 9),按升序 示例

"21AxBz" -> "xzAB12"

  • since input does not contain repeating characters, you only need 1 block由于输入不包含重复字符,您只需要 1 个块

"abacad" -> "abcd-aa"

  • character "a" repeats 3 times, thus 3 blocks are needed字符“a”重复 3 次,因此需要 3 个块

"" -> ""

  • an empty input should result in an empty output空输入应该导致空输出

What I've tried actually works for the given test cases:我尝试过的实际上适用于给定的测试用例:

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

But fails when there are any repeating characters, besides in the test cases:但是当有任何重复字符时失败,除了在测试用例中:

 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(/[az]/g).join(''); let upper = s.match(/[AZ]/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"));

And actually, I'm not sure what's wrong with it, because I don't know how to unlock any other tests on Codewars.实际上,我不确定它有什么问题,因为我不知道如何在 Codewars 上解锁任何其他测试。

You can see that what I'm trying to do, is find the repeating character, get all characters that are not the repeating character, and slice the string from starting point up until the 2 instance of the repeating character, and then add on the remaining repeating characters at the end, separated by dashes.您可以看到我正在尝试做的是找到重复字符,获取所有不是重复字符的字符,然后将字符串从起始点切片直到重复字符的第 2 个实例,然后添加末尾剩余的重复字符,以破折号分隔。

Any other suggestions for how to do this?关于如何做到这一点的任何其他建议?

The first obvious error I can see is let repeatCount = s.match(/[repeatRegex]/gi).length;我能看到的第一个明显错误是let repeatCount = s.match(/[repeatRegex]/gi).length; . . What you really want to do is:你真正想做的是:

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

The next is that you only look at one of the repeating characters, and not all of them, so you won't get blocks of the correct form, so you'll need to loop over them.接下来是您只查看重复字符中的一个,而不是全部,因此您不会得到正确形式的块,因此您需要遍历它们。

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

When you're building the block, you've decided to focus on everything that's not "a".当您构建块时,您已决定专注于不是“a”的所有内容。 I'm guessing that's not what you originally wrote, but some debugging code, to work on that one sample input.我猜这不是您最初编写的内容,而是一些调试代码,用于处理该示例输入。

If I understand your desire correctly, you want to take all the non-repeating characters and smoosh them together, then take the first instance of the first repeating character and stuff that on the front and then cram the remaining instances of the repeating character on the back, separated by - .如果我正确理解你的愿望,你想把所有不重复的字符放在一起,然后把第一个重复字符的第一个实例放在前面,然后把重复字符的剩余实例塞进前面返回,以-分隔。

The problem here is that the first repeating character might not be the one that should be first in the result.这里的问题是第一个重复的字符可能不是结果中应该排在第一位的字符。 Essentially you got lucky with the repeating character being a .从本质上讲,你很幸运与重复的字符是a

Fixing up your code, I would create an array and build the blocks up individually, then join them all together at the end.修复您的代码,我将创建一个数组并单独构建块,然后在最后将它们连接在一起。

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

However, this leaves me with a problem of how to build the final string with the non-repeating characters included, all in the right order.但是,这给我留下了一个问题,即如何以正确的顺序构建包含非重复字符的最终字符串。

So, to start with, let's make the initial string.所以,首先,让我们制作初始字符串。 To do so we'll need a custom sort function (sorry, it's pretty verbose. If only we could use regular ASCII ordering):为此,我们需要一个自定义排序函数(抱歉,它太冗长了。如果我们可以使用常规的 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;
}

Then create the head of the final output:然后创建最终输出的头部:

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

The Set creates a set of unique elements, ie no repeats. Set创建一组独特的元素,即没有重复。

Because we've created the head string, when creating the blocks of repeated characters, we'll need one fewer than the above loop gives us, so we'll be using s.match(repeatRegex).length-1 .因为我们已经创建了头字符串,所以在创建重复字符块时,我们需要比上面循环给我们的少一个,所以我们将使用s.match(repeatRegex).length-1

I get the desire to short circuit the complicated bit and return quickly when there are no repeated characters, but I'm going to remove that bit for brevity, and also I don't want to deal with undefined values (for example try '123' as your input).我希望将复杂的位短路并在没有重复字符时快速返回,但为了简洁起见,我将删除该位,而且我不想处理未定义的值(例如尝试'123'作为您的输入)。

Let's put it all together:让我们把它放在一起:

 function lowerUpperNumber(a, b) { if(a.match(/[az]/) && b.match(/[A-Z0-9]/)) { return -1; } else if(a.match(/[AZ]/) && (b.match(/[0-9]/) || b.match(/[az]/))) { if(b.match(/[0-9]/)) { return -1; } else if(b.match(/[az]/)) { 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(''));

You'll see I've not bothered generating the characters that repeat, because I can just skip the ones that don't.你会看到我没有费心去生成重复的字符,因为我可以跳过那些不重复的字符。

For funzies, here's how I would have approached the problem:对于funzies,这是我解决问题的方法:

 const isLower = new RegExp('[az]'); const isUpper = new RegExp('[AZ]'); 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(''));

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM