DEV Community

dev.to staff
dev.to staff

Posted on

Daily Challenge #167 - Return String As Sorted Blocks

Task

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

  • "21AxBz" -> "xzAB12" - since input does not contain repeating characters, you only need 1 block

  • "abacad" -> "abcd-a-a" - character "a" repeats 3 times, thus 3 blocks are needed

  • "" -> "" - an empty input should result in an empty output


This challenge comes from airatmulk on CodeWars. Thank you to CodeWars, who has licensed redistribution of this challenge under the 2-Clause BSD License!

Want to propose a challenge idea for a future post? Email yo+challenge@dev.to with your suggestions!

Top comments (4)

Collapse
 
craigmc08 profile image
Craig McIlwrath

Solution in Haskell.

import Data.List (group, sortBy, intercalate)

between :: (Ord a) => a -> a -> a -> Bool
between lo hi v = v >= lo && v <= hi

compareLetters :: Char -> Char -> Ordering
compareLetters a b
  | setA < setB = LT
  | setA > setB = GT
  | otherwise   = compare a b
  where setOf c = if between 'a' 'z' c then 0
                  else if between 'A' 'Z' c then 1
                  else 2
        setA    = setOf a
        setB    = setOf b

blocks :: [[a]] -> [[a]]
blocks [] = []
blocks xss = let ys  = map head xss
                 yss = filter (not . null) $ map tail xss
             in  ys : blocks yss

blockify :: (Eq a) => ([a] -> [a]) -> [a] -> [[a]]
blockify sort = blocks . group . sort

blockifyString :: String -> String
blockifyString = intercalate "-" . blockify (sortBy compareLetters)

It sorts the input string, then groups it into arrays of each piece (e.g. "abbcddde" -> ["a", "bb", "c", "ddd", "e"]. The blocks method combines corresponding elements of the sub-arrays after grouping (continuing the example -> ["abcde", "bd", "d"]). Then the strings are joined by a hyphen. My least favorite part of my solution is the compareLetters function which determines the order of 2 letters for letters. I couldn't come up with a better way to do this.

Collapse
 
candidateplanet profile image
lusen / they / them 🏳️‍🌈🥑 • Edited

in python

def blocks(str):
  # const - reference for extracting blocks
  order = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"

  # count occurences of characters in str
  mapping = {}
  # build mapping
  for s in str:
    if s not in mapping:
      mapping[s] = 0
    mapping[s] += 1

  # iterate through mapping to create each block, decrementing the counts
  # with each pass. when the count is 0, delete the key,value pair.
  # stop iterating when there are no more values to create blocks from
  blocks = []
  while mapping.values():
    block = []
    for o in order:
      if o in mapping:
        block.append(o)
        mapping[o] -= 1
        if mapping[o] == 0:
          del mapping[o]
    blocks.append(''.join(block))

  return '-'.join(blocks)

print(blocks("21AxBz"), "xzAB12")
print(blocks("abacad"), "abcd-a-a")
print(blocks(""), "")
print(blocks("21BxAzAasfdaXZas"), "adfsxzABXZ12-asA-a")
Collapse
 
amcmillan01 profile image
Andrei McMillan

python

import re

def sorted_blocks(input_str):
    str_usage = {}
    arr_list = {}

    upper_case = re.findall('[A-Z]', input_str)
    lower_case = re.findall('[a-z]', input_str)
    numbers = re.findall('[0-9]', input_str)

    upper_case.sort()
    lower_case.sort()
    numbers.sort()

    for dataset in [lower_case, upper_case, numbers]:

        for item in dataset:
            if str_usage.get(item) is not None:
                str_usage[item] += 1
            else:
                str_usage[item] = 1

            index = str_usage[item]

            arr_list.setdefault(index, '')
            arr_list[index] += item

    print 'input    : "' + input_str + '"'
    print 'block(s) : "' + '-'.join(arr_list.values()) + '"'
    print ' '

# tests
sorted_blocks('21AxBz')
sorted_blocks('21BxAzAasfdaXZas')
sorted_blocks('abacad')
sorted_blocks('')

# output

# input    : "21AxBz"
# block(s) : "xzAB12"
# 
# input    : "21BxAzAasfdaXZas"
# block(s) : "adfsxzABXZ12-asA-a"
# 
# input    : "abacad"
# block(s) : "abcd-a-a"
# 
# input    : ""
# block(s) : ""


Collapse
 
mellen profile image
Matt Ellen

Someone ask on SO, and I answered:

For funzies, here's how I would have approached the problem:

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) &&