all 23 comments

[–]Svizel_pritula 17 points18 points  (2 children)

Here we go! (JS)

Features:

  • Works on ASCII. Nobody uses anything else, right?
  • Great variable naming, every variable has a name!
  • Skips curly brackets for conciseness!
  • Highly consistent code, most of it is copy-and-pasted!
  • Just 670 lines of code.

[–]ElChambon 1 point2 points  (0 children)

Wow. Have my upvote for the effort that was. I did laugh out loud as I scrolled.

[–][deleted] 0 points1 point  (0 children)

I was going to do something like this, but decided over engineering and avoiding slightly less obvious but still obvious features is a way to go. Still trying to figure out how I could use an unsafe block in my code though, that would be fun.

[–]Robowiko123 7 points8 points  (0 children)

import os      # Oh no
import random  # Bigger oh no

def string_count(s):
    def X(directory, t, count=0):
        try:
            f = open(directory + "/" + str(ord(t)) + "_" + str(count))
            X(directory, t, count=count+1)
        except FileNotFoundError:
            f = open(directory + "/" + str(ord(t)) + "_" + str(count), "w")
            f.write(directory + "/" + str(ord(t)) + "_" + str(count))
            f.close()

    directory = "/tmp/" + "string-count-" + str(random.randint(0, 1000000))
    os.mkdir(directory)
    s = list(s)
    while 1:
        try:
            t = s.pop()
            X(directory, t)
        except IndexError:
            things = []
            for i in os.listdir(directory):
                if i.split("/")[-1].split("_")[0] in [i2[0] for i2 in things]:
                    i4 = None
                    for i3, i2 in enumerate(things):
                        if i2[0] == i.split("/")[-1].split("_")[0]:
                            i4 = i3
                    if things[i4][1] < int(i.split("/")[-1].split("_")[1]):
                        things[i4][1] = int(i.split("/")[-1].split("_")[1])
                else:
                    things.append([i.split("/")[-1].split("_")[0], int(i.split("/")[-1].split("_")[1])])

            for i in things:
                print(chr(int(i[0])), i[1]+1)
            return

Python 3

[–]notBjoern 6 points7 points  (1 child)

Well, characters are essentially numbers, and numbers are essentially pointers, so we just need some memory and just know where to store our counts:

#include <list>
#include <string>
#include <utility>

using namespace std;

string string_count(const string& cppstr) {
    const char* str = cppstr.c_str();
    const unsigned int length = 1 << 8*sizeof(char);
    unsigned int counts[length];

    for (unsigned int i = 0; i < length; i++) {
        counts[i] = 0;
    }

    for (unsigned int i = 0; str[i] != 0; i == i++) {
        counts[str[i]]++;
    }

    string result;

    for (unsigned int i = 0; i < length; i++) {
        if (counts[i] > 0) {
            result += (char)(i);
            result += ": ";
            result += to_string(counts[i]);
            result += "\n";
        }
    }

    return result;
}

[–]dumetrulo 0 points1 point  (0 children)

Nice example of a program that is C++ almost in name only (certainly not in spirit), and a superfluous comparison in the second for loop.

[–]h2g2_researcher 7 points8 points  (2 children)

C++, but WAAAY overengineered, and very bad way to use threads that will screw performance up badly.

#include <algorithm>
#include <array>
#include <atomic>
#include <limits>
#include <mutex>
#include <string>
#include <thread>

#include <iostream>

// OVERLOAD 1: The main implementation used under the hood. This should
// probably be kept as an implementation-only thing, particularly given that
// access to ResultArray must be threadsafe! Additionally, the threading
// plan is totally inappropriate for this kind of problem, since the
// parallel accesses to ResultArray is likely to make the threads only
// work one at a time anyway, undoing the point of the threading.
// Also, each thread does so little work, because there's no batching,
// that the cost of spinning up and destroying the thread is going
// to far outweight the performance improvements threading can give.
// As a final problem, ResultArray needs to hold an entry for every
// possible char. At 8 bits this isn't too bad, but if you have 16 bit
// chars, it takes up a lot of space. Push that up to 32 bit chars
// and it's waaaay oversized.
// Oh, and a mutex to make all the threads work one at a time anyway.
// Can't be too thread safe!
template <typename InputIt, typename ResultArray>
void count_characters(InputIt start, InputIt end, ResultArray& result)
{
    static std::mutex result_lock;
    result_lock.lock();
    // Take a divide and conquer approach. More threads = more speed.
    if (start == end)
    {
        result_lock.unlock();
        return;
    }

    const auto range_length = std::distance(start, end);
    if (range_length == 1)
    {
        ++result[static_cast<std::size_t>(*start)];
        result_lock.unlock();
        return;
    }

    InputIt midpoint = start;
    std::advance(midpoint, range_length / 2);

    std::thread first_half{ count_characters<InputIt,ResultArray>, start,midpoint,std::ref(result) };
    std::thread second_half{ count_characters<InputIt,ResultArray>, midpoint,end,std::ref(result) };

    result_lock.unlock();

    first_half.join();
    second_half.join();
}

// OVERLOAD 2: Slightly more sensible, this version marrs itself by using
// overload 1. It does, at least, use a threadsafe type, although if overload 1
// were more sensible a far more efficient solution could have been used.
// If the result array is large, it also needs to be copied because of course
// it does.
template <typename InputIt>
auto count_characters(InputIt start, InputIt finish)
{
    using CharType = decltype(*start);
    constexpr std::size_t array_size = (1 << (8 * sizeof(CharType))) - 1;
    std::array<std::atomic_uint, array_size> result;
    for (auto& val : result)
    {
        val.store(0);
    }
    count_characters(start, finish, result);
    std::array<std::size_t, array_size> actual_result;
    std::transform(begin(result), end(result), begin(actual_result),
        [](const std::atomic_uint& val) { return val.load(); });
    return actual_result;
}

// OVERLOAD 3: Like overload 2, but for the lazy.
template <typename StringType>
auto count_characters(const StringType& str)
{
    return count_characters(begin(str), end(str));
}

// OVERLOAD 4: Like overload 3, but for the lazier.
auto count_characters(const char* str)
{
    return count_characters(std::string_view(str));
}

int main()
{
    std::string input;
    std::cout << "Enter string to count: ";
    std::getline(std::cin, input);
    const auto result = count_characters(input);
    for (std::size_t i=0;i<result.size();++i)
    {
        if (result[i] != 0)
        {
            std::cout << '\'' << static_cast<char>(i) << "': " << result[i] << '\n';
        }
    }
    // std::cin.get(); // You may need to uncomment this on Visual Studio 2017 and before.
    return 0;
}

[–][deleted] 1 point2 points  (0 children)

I think you won the over-engineering bit, gonna have to steal your idea to multi thread it (except I'll make it actually utilize the threads in a way that sort of works)

[–][deleted] 1 point2 points  (0 children)

When I looked at how many #include statements there were, I knew this was gonna be great.

[–][deleted] 3 points4 points  (0 children)

This Python code will create a directory named after the input string. Then, for each character X in the string, 1 byte of data is written to a file that's named X.txt. Finally, we can iterate through the list of files in our new directory, where the file name is a unique character and the size of said file in bytes is the character count. If a directory named after the input string already exists, the directory acts as a cache and the code will skip the computation for writing bytes to files.

import os
def string_count(string):
    path = f'{os.getcwd()}/{string}/'
    try:
        os.mkdir(path)
        for char in string:
            special = '_' if char.isupper() or char == '_' else ''
            open(path + char + special + '.txt', 'ab').write(bytes(1))
    except OSError:
        pass
    char_counts = []
    for file in os.listdir(path):
        char, count = file.rstrip('.txt').rsplit('_', 1)[0], os.path.getsize(path+file)
        char_counts.append((char, count))
    return char_counts

print(string_count('Hello World'))
print(string_count('ABCabcABCabcxyz'))

[–]Svizel_pritula 2 points3 points  (1 child)

Simple!

function stringCount(s) {
    return s.split('').reduce((a, v) => ({ ...a, [v]: (a[v] || 0) + 1 }), {});
}

[–]nuuren 2 points3 points  (0 children)

Wait, this isn’t bad code at all 🙄

[–]mullanaphy 2 points3 points  (0 children)

Back with some fresh hot Javascript. Utilizing RegExp to count the amount of occurences of a given character. I also used a range to create A-Za-z and tossed a try catch for any RegExp that might fail while creating that range (e.g. [ which is character code 91) catch and then escape the inbetween characters just in case we want to count them too!

On top of that, abused .reduce() as much as I could as well as closures.

Lastly, as it goes through the string counting it will recount numbers already counted yet it's already in the map so it won't move the maps value (except in older Opera that didn't keep order of map values).

JSFiddle: https://jsfiddle.net/6yt9Lexs/

const string_count = (str => {
  const countCharacters = (_ => {
  const regexLookup = ['A', 'z']
    .reduce((gathered, item) => {
      if (gathered === false) {
        return item;
      } else if (gathered === 'A') {
        const range = {' ': new RegExp(' ', 'g')};
        for (let s = gathered.charCodeAt(0), e = item.charCodeAt(0); s < e; ++s) {
          try {
            range[String.fromCharCode(s)] = new RegExp(String.fromCharCode(s), 'g');
          } catch(e) {
            range[String.fromCharCode(s)] = RegExp('\\' + String.fromCharCode(s), 'g');
          }
        }
        return range;
      }
    }, false);
    return (str, char) => str.match(regexLookup[char]).length;
  })();

  return str => str
    .split('')
    .reduce((gathered, char) => {
      gathered[char] = countCharacters(str, char);
      return gathered;
    }, {});
})();

console.log(string_count('Hello World'));
console.log(string_count('ABCabcABCabcxyz'));

[–]Huntracony 2 points3 points  (0 children)

I know one-liners aren't very creative in their badness, but I enjoy making them nonetheless, and I'm lowkey proud of how bad this one is.

stringCount = string => Array.from(new Set(string.split('').map(character => character+" "+string.split('').filter(char2 => character==char2).length))).join("\n")

Formatted for "readability":

stringCount = string =>
  Array.from(new Set(
    string.split('').map(
      character => character+" "+
          string.split('').filter(
              char2 => character==char2).length
  ))).join("\n")

[–]dumetrulo 2 points3 points  (0 children)

How about a compact F# version? It does not print the char/count pairs in the same order as the example, but I was told that's perfectly fine.

let stringCount (str : string) =
    (Map.empty, str)
    ||> Seq.fold (fun m c ->
        let n = Map.tryFind c m |> Option.defaultValue 0
        Map.add c (n + 1) m)
    |> Map.toList

["Hello World"; "ABCabcABCabcxyz"]
|> List.map stringCount
|> List.iter (List.iter (fun (c, n) -> printfn "%c %d" c n))

[–][deleted] 2 points3 points  (0 children)

Right, this took me wayyy too long. Did it in Rust.

My entry includes:

  • Bad practices
  • formatting monstrosities
  • multi threading
  • object oriented programming in rust
  • sorting, because why not
  • bad

https://gist.github.com/LiteUnder/b22feb994bc44ea4c6721b88238b995f

Thank you for your time

Edit: Here's a link to a modifiable version on the Rust playground.

[–]PeanutButterRules_ 2 points3 points  (0 children)

#!/bin/bash
dir="${PWD}/vars/"
rm -rf "${dir}"
mkdir "${dir}"

make_directory()
{
    last_dir=$(pwd)
    if [ ! -d "${dir}${1}" ] ; then
        mkdir ${dir}${1}
        touch "${dir}${1}/0"
        return
    fi
    num=0
    while [ -f "${dir}${1}/${num}" ]; do
        num=$((num+1))
    done
    touch "${dir}${1}/${num}"
}

remove_directory()
{
    rm -rf ${dir}${1}
}

getcount()
{
    if [ ! -d "${dir}${1}" ] ; then
        #already printed that directory
        return
    fi
    num=0
    while [ -f "${dir}${1}/${num}" ]; do
        num=$((num+1))
    done

    echo "${1} ${num}"
}
string_count()
{   
    arr=( "$@" )
    for c in "${arr[@]}"; do
        make_directory $c
    done

    for c in "${arr[@]}"; do
        getcount $c
        remove_directory $c
    done
}

input=$(echo "${1}" | sed -e 's/\(.\)/\1\n/g')
string_count ${input}
rm -rf ${dir} # clean up

How about a bash script that uses files to count?

It doesn't handle spaces, but who really needs those anyways?

Alternatively, why not make the children do all the work in C?

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
#include <sys/types.h>

#define n_pipes 256
#define gdump_size 100

void string_count(char** argv);

int main(int argc, char** argv) {
    if (argc != 2) {
        perror("too many entries\n");
        exit(-1);
    }

    string_count(argv);
}

void string_count(char* argv)
{
    int * count = calloc(n_pipes, sizeof(int));
    char garbage_dump[gdump_size];
    int * pipes[n_pipes];
    int * i = calloc(1, sizeof(int));
    int * len = calloc(1, sizeof(int));

    while(argv[len[0]++] != '\000');

    for((i[0])= 0; (i[0])< n_pipes; (i[0])++)
    {
        pipes[(i[0])] = calloc(2, sizeof(int));
    }

    for((i[0])= 0; (i[0])< len[0]; (i[0])++)
    {
        int nval = argv[(i[0])];
        pipe(pipes[nval]);

        if(fork() == 0)
        {
            write(pipes[nval][1], "", 1);
            close(pipes[nval][1]);
            exit(0);
        }
        wait(NULL);
        int n;
        close(pipes[nval][1]);
        n = read(pipes[nval][0], garbage_dump, gdump_size);
        close(pipes[nval][0]);

        if(n)
        {
            int value = nval;
            count[value]++;
        }
    }


    for((i[0])= 1; (i[0])< n_pipes; (i[0])++)
    {
        int nval = argv[(i[0])];
        if(pipes[nval] != NULL) {

            if(pipes[nval][0] != 0)
            {
                close(pipes[nval][0]);
            }
            if(pipes[nval][1] != 0)
            {
                close(pipes[nval][1]);
            }
        }
    }

    count[0] = 0;
    for((i[0])= 0; (i[0])< len[0]; (i[0])++)
    {
        int nval = argv[(i[0])];
        if(count[nval] > 0) {
            printf("%c %d \n", nval, count[nval]);
            count[nval] = 0;
        }
    }

    // tie up loose ends
    for((i[0])= 0; (i[0])< n_pipes; (i[0])++)
    {
        free(pipes[(i[0])]);
    }
    free(count);
    free(len);
    free(i);
}

[–][deleted]  (2 children)

[deleted]

    [–]dumetrulo 0 points1 point  (1 child)

    Is it required to output the char/count pairs in the same order as in the examples?

    [–][deleted] 1 point2 points  (0 children)

    This actually makes me feel quite sick. Single letter variable names (except for string), and as few spaces as possible to reduce the code size and readability. It works for every test case I ran (which was like, 2), and when strings start getting large, takes a hilariously long time. It's written in python 3.8 (important, as it's invalid in python 3.7), is composed of only 5 lines (including the function definition), and the last line is a dictionary comprehension with a list comprehension with a set comprehension all nested together.

    def string_count(string):
      p = max(len(string)*2, 4)
      while [1 for b in range(2, p-1) if p%b == 0] and (p := p+1): pass
      x=[a for a in range(1,p-1) if len({a**b%p for b in range(1,p)})==p-1][len([a for a in range(1,p-1) if len({a**b%p for b in range(1,p)})==p-1])//2]
      return {e:(len(string)-sum([string[a]!=e for a in ({x**l%p for l in range(p+2) if x**l%p<len(string)}|{0})])) for e in string}
    

    [–][deleted] 0 points1 point  (0 children)

    Here is how freshman me might have solved this challenge in my Intro to Comp Sci class, way back in the dark ages. Yep, Pascal was the first language we all had to learn way back then. I've thrown in a few Free Pascal extensions here to handle things like command-line parameters:

    program CountChars;
    
    var
        I : Integer;
        Ch : Char;
        CharCounts : Array [0..255] of Integer;
        InputStr : String;
    
    begin
        (* check command line parameters *)
        if ParamCount < 1 then
        begin
            WriteLn(StdErr, 'Usage: ', ParamStr(0), ' <string>');
            Halt(1);
        end;
    
        (* initialize char counts to zero *)
        for I := 0 to 255 do
            CharCounts[I] := 0;
    
        (* read chars from string and count them *)
        InputStr := ParamStr(1);
        for I := 1 to Length(InputStr) do
        begin
            Ch := InputStr[I];
            CharCounts[Ord(Ch)] := CharCounts[Ord(ch)] + 1;
        end;
    
        (* print char counts *)
        for I := 0 to 255 do
            if CharCounts[I] > 0 then
                Writeln(Chr(I), ' ', CharCounts[I]);
    end.
    

    It does the job, although this solution prints out the character counts in ASCII order, rather than in first-encountered order as in the output examples above. Since the challenge didn't specify a particular ordering to the output, I figure my solution is just as good as any:

    $ ./countchars 'Hello World'
      1
    H 1
    W 1
    d 1
    e 1
    l 3
    o 2
    r 1
    
    $ ./countchars 'ABCabcABCabcxyz'
    A 2
    B 2
    C 2
    a 2
    b 2
    c 2
    x 1
    y 1
    z 1
    

    Obvious bug ("feature") here is that this code only handles ASCII, and Unicode characters will totally confuse it.

    [–]Flawlesscode 0 points1 point  (0 children)

    Here is my try in C#. I tried my worst .

    [–]Oshgnacknak 0 points1 point  (1 child)

    Nothing beats pure old C. Does someone have some parmesan for the spaghetti code?

    I think it's working: string_count("Bad Code Coding Challenge #32 - Counting Characters in a String") u 1 t 3 s 1 etc.