Возвращаемая строка как отсортированные блоки - Codewars Challenge - JavaScript - PullRequest
1 голос
/ 08 января 2020

Я пытался решить этот вызов codewars . Идея состоит в том, чтобы вернуть строку, перестроенную в соответствии с ее иерархией или разделенную на куски в соответствии с повторяющимся символом.


В качестве ввода вы получите строку, состоящую из строчных букв, заглавных букв и цифр. Ваша задача - вернуть эту строку в виде блоков, разделенных тире ("-"). Элементы блока должны быть отсортированы по иерархии, перечисленной ниже, и каждый блок не может содержать несколько экземпляров одного и того же символа.

Иерархия:

строчные буквы (a - z ), в алфавитном порядке c заглавными буквами (A - Z), в алфавитном порядке c порядковыми цифрами (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 экземпляров повторяющегося символа. , а затем добавьте оставшиеся повторяющиеся символы в конце, разделенные черточками.

Любые другие предложения о том, как это сделать?

Ответы [ 2 ]

1 голос
/ 06 февраля 2020

Для забав, вот как я бы подошел к проблеме:

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(''));
1 голос
/ 05 февраля 2020

Первая очевидная ошибка, которую я вижу, это let repeatCount = s.match(/[repeatRegex]/gi).length;. То, что вы действительно хотите сделать, это:

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

Следующим является то, что вы смотрите только на один из повторяющихся символов, а не на всех, поэтому вы не получите блоки правильной формы, поэтому вам нужно l oop над ними.

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

Когда вы строите блок, вы решили сосредоточиться на всем, что не "а". Я предполагаю, что это не то, что вы изначально написали, а какой-то отладочный код для работы с этим одним примером ввода.

Если я правильно понимаю ваше желание, вы хотите взять все неповторяющиеся символы и smoo sh их вместе, затем возьмите первый экземпляр первого повторяющегося символа и прочее, что находится впереди, а затем втисните оставшиеся экземпляры повторяющегося символа сзади, разделенные -.

Проблема здесь является то, что первый повторяющийся символ может не быть тем, который должен быть первым в результате. По сути, вам повезло с повторяющимся символом 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;
}

Затем создайте заголовок конечного результата:

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

Set создает набор уникальных элементов, т.е. не повторяется.

Поскольку мы создали строку заголовка, при создании блоков повторяющихся символов нам понадобится на один меньше, чем вышеприведенное l oop дает нам, поэтому мы будем использовать 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(''));

Вы увидите, что я не потрудился генерировать повторяющиеся символы, потому что я могу просто пропустить те, которые этого не делают.

...