DEV Community

dev.to staff
dev.to staff

Posted on

Daily Challenge #259 - Duplicate Encoder

The goal of this exercise is to convert a string to a new string where each character in the new string is "(" if that character appears only once in the original string, or ")" if that character appears more than once in the original string. Ignore capitalization when determining if a character is a duplicate.

Examples
"Success" => ")())())"
"(( @" => "))(("

Tests
"din"
"recede"

Good luck!


This challenge comes from obnounce 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 (27)

Collapse
 
namhle profile image
Nam Hoang Le

Javascript solution:

const uniq = (str, chr) => str.indexOf(chr) === str.lastIndexOf(chr);

const encodeDuplicate = str => [...str].map(chr => uniq(str.toLowerCase(), chr)? "(" :")").join``; 
Collapse
 
thomasledoux1 profile image
Thomas Ledoux

Neat!
What is this syntax at the end?

join``

I always use

.join('').

Collapse
 
namhle profile image
Nam Hoang Le • Edited

That is Tagged template literals, to save 2 character :D

Thread Thread
 
jsn1nj4 profile image
Elliot Derhay

I forgot all about that syntax. I've probably only used it a few times.

Collapse
 
jvanbruegge profile image
Jan van Brügge • Edited

My Haskell solution:

import Data.Char (toLower)

encode str = (\n -> if n > 1 then ')' else '(' )
        <$> fmap (\c -> length $ filter ((==) c) str') str'
    where str' = fmap toLower str
Collapse
 
aminnairi profile image
Amin • Edited

Go

$ mkdir -p $GOPATH/src/encoder
$ cd $GOPATH/src/encoder
$ touch encoder.go
// Set of utilities to encode a string into different formats.
package encoder

import (
    "unicode"
    "strings"
)

// Encode a given string into a format comprised of left and right parens. If one character is present multiple times in the string, the left parens will be used to encode this character, otherwise, the right parens will be used.
func EncodeDuplicateParens(input string) string {
    encoded := strings.Builder{}
    occurrences := make(map[rune]int)

    for _, character := range input {
        occurrences[unicode.ToLower(character)]++
    }

    for _, character := range input {
        if occurrences[unicode.ToLower(character)] > 1 {
            encoded.WriteRune(')')
        } else {
            encoded.WriteRune('(')
        }
    }

    return encoded.String()
}
$ touch encoder_test.go
package encoder_test

import (
    "encoder"
    "fmt"
    "testing"
)

func TestWithMultipleStrings(t *testing.T) {
    valuesExpectations := map[string]string{
        "(( @": "))((",
        "Success": ")())())",
        "din": "(((",
        "recede": "()()()",
    }

    for value, expectation := range valuesExpectations {
        result := encoder.EncodeDuplicateParens(value)

        if expectation != result {
            t.Errorf("Expected encode.EncodeDuplicateParens(%q) to equal %q but got %q.", value, expectation, result)
        }
    }
}

func BenchmarkEncodeDuplicateParens(b *testing.B) {
    for index := 0; index < b.N; index++ {
        encoder.EncodeDuplicateParens("Success")
    }
}

func ExampleEncodeDuplicateParens() {
    values := []string{
        "(( @",
        "Success",
        "din",
        "recede",
    }

    for _, value := range values {
        fmt.Println(encoder.EncodeDuplicateParens(value));
    }

    // Output: ))((
    // )())())
    // (((
    // ()()()
}

$ go test -cover -bench .
goos: linux
goarch: amd64
pkg: encoder
BenchmarkEncodeDuplicateParens-4         4961448               256 ns/op
PASS
coverage: 100.0% of statements
ok      encoder 1.516s
Collapse
 
rafaacioly profile image
Rafael Acioly

Python solution 🐍

from collections import Counter

ONCE, MANY = '(', ')'


def parse(text: str) -> str:
  normalized_text = text.lower()
  letter_counter = Counter(normalized_text)

  result: str = normalized_text
  for letter in normalized_text:
    result = result.replace(
      letter,
      MANY if letter_counter.get(letter) > 1 else ONCE
    )

  return result
Collapse
 
peter279k profile image
peter279k

Here is the simple approach with nested for loops with PHP:

function duplicate_encode($word){
  $index = 0;
  $word = mb_strtolower($word);
  $result = $word;
  for ($index=0; $index<mb_strlen($word); $index++) {
    $isDuplicated = false;
    for ($secondIndex=$index+1; $secondIndex<mb_strlen($word); $secondIndex++) {
      if ($word[$index] === $word[$secondIndex]) {
        $result[$index] = ")";
        $result[$secondIndex] = ")";
        $isDuplicated = true;   
      }
    }

    if ($isDuplicated === false && $result[$index] === $word[$index]) {
      $result[$index] = "(";
    }
  }

  return $result;
}
Collapse
 
gnunicorn profile image
Benjamin Kampmann

In Rust – playground link (with tests)

pub fn dub_enc(inp: String) -> String {
    let lowered = inp.to_lowercase();
    lowered
        .chars()
        .map(|c| {
            let mut finder = lowered.chars().filter(|r| *r == c);
            finder.next(); // exists
            if finder.next().is_some() { // found at least once more
                ")"
            } else {
                "("
            }
        })
        .collect()
} 
Collapse
 
lexlohr profile image
Alex Lohr

Why not use std::str::matches to iterate over the same chars?

Collapse
 
gnunicorn profile image
Benjamin Kampmann

I suppose that is a valid alternative. Just not the first thing that came to mind ...

Thread Thread
 
lexlohr profile image
Alex Lohr

I just love how Rust has a helpful method for about everything in its std/core libs.

Collapse
 
jpantunes profile image
JP Antunes

A simple (and somewhat slow) JS solution:

const encodeDuplicate = str => {
    //make a frequency map
    const freqMap = [...str.toLowerCase()].reduce((acc, val) => {
        acc[val] = acc[val] + 1 || 1;
        return acc;
    }, {});
    //return the mapped str 
    return [...str.toLowerCase()]
            .map(e => freqMap[e] == 1 ? '(' : ')')
            .join('');
}
Collapse
 
miketalbot profile image
Mike Talbot ⭐

Not that slow :) Better than anything that searched/scanned or did an indexOf for sure! You know I like that || 1 as well, I always do (acc[val] || 0) + 1 - but that is much neater. Borrowing that.

Collapse
 
namhle profile image
Nam Hoang Le

Same to me || 0

Collapse
 
jingxue profile image
Jing Xue • Edited

Python:



from collections import Counter


def duplicate_encoder(s: str) -> str:
    all_lower = s.lower()
    counter = Counter(all_lower)
    return ''.join([')' if counter.get(c) > 1 else '(' for c in all_lower])
Collapse
 
arthelon profile image
Arthelon

TypeScript:

function isUnique(char: string, fullString: string): boolean {
  return fullString.indexOf(char) === fullString.lastIndexOf(char);
}

function duplicateEncoder(input: string): string {
  const lowercase = input.toLowerCase();
  return lowercase
    .split("")
    .map((char) => (isUnique(char, input) ? "(" : ")"))
    .join("");
}

Collapse
 
patricktingen profile image
Patrick Tingen • Edited
/* Progress 4GL Daily Challenge #259
*/
FUNCTION dupDecode RETURNS CHARACTER (pcString AS CHARACTER):
  DEFINE VARIABLE i AS INTEGER NO-UNDO.
  DO i = 1 TO LENGTH(pcString):
    pcString = REPLACE( pcString
                      , SUBSTRING(pcString,i,1)
                      , STRING(NUM-ENTRIES(pcString,SUBSTRING(pcString,i,1)) = 2,'(/)')
                      ).
  END.
  RETURN pcString.
END FUNCTION.

MESSAGE dupDecode('Success')
  VIEW-AS ALERT-BOX INFORMATION BUTTONS OK.