Skip to content

Commit

Permalink
New challenge: Split on New Chars
Browse files Browse the repository at this point in the history
  • Loading branch information
shape-warrior-t committed Jul 7, 2024
1 parent 45e94c1 commit bea3bd0
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Challenges:
- [Rainfall + tests](rust_challenges/src/rainfall.rs) [Rust, unit testing, data structures & algorithms, dynamic programming, complexity analysis, macros]
- [Island Sizes + tests](rust_challenges/src/island_sizes.rs) [Rust, unit testing, data structures & algorithms, flood fill, complexity analysis, macros, object-oriented programming]
- [Square-difference-free](typescript-challenges/code/square-difference-free.ts) ([tests](typescript-challenges/code/square-difference-free.test.ts)) [TypeScript, data structures & algorithms, object-oriented programming]
- [Split on New Chars](typescript-challenges/code/split-on-new-chars.ts) ([tests](typescript-challenges/code/split-on-new-chars.test.ts)) [TypeScript, property-based testing, data structures & algorithms]

Helper code:

Expand Down
57 changes: 57 additions & 0 deletions typescript-challenges/code/split-on-new-chars.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/**
* Property-based test for Split on New Chars,
* ensuring that the output meets the specifications outlined in the challenge description.
*/

import { expect, test } from '@jest/globals';
import fc from 'fast-check';
import splitOnNewChars from './split-on-new-chars';
import { uniqueEverseen } from 'itertools';

/**
* Arbitrary for inputs to `splitOnNewChars`.
*
* Characters are taken from a set of size 5
* so that generated strings are more likely to repeat characters.
*/
const testString = fc.stringOf(fc.constantFrom('a', 'b', 'c', 'd', 'e'));

test('satisfies required properties', () => {
fc.assert(
fc.property(testString, (str: string) => {
const arr = splitOnNewChars(str);
expect(arr.join('')).toStrictEqual(str);
expect(indicesOfSplit(arr)).toStrictEqual(
charFirstOccurrences(str),
);
}),
);
});

/**
* Given a split of a string,
* return the index at which each substring begins in the original string.
*
* Example: `['aa', 'baba', 'ccabac'] => [0, 2, 6]` \
* In `'aababaccabac'` (`['aa', 'baba', 'ccabac'].join('')`), \
* `'aa'` begins at index 0, `'baba'` begins at index 2, and `'ccabac'` begins at index 6.
*/
function indicesOfSplit(arr: string[]): number[] {
const indices = [];
let currIndex = 0;
for (const substring of arr) {
indices.push(currIndex);
currIndex += substring.length;
}
return indices;
}

/**
* Return the index of the first occurrence of each character in the given string.
*
* Example: `'aababaccabac' => [0, 2, 6]` \
* `'a'` first occurs at index 0, `'b'` first occurs at index 2, and `'c'` first occurs at index 6.
*/
function charFirstOccurrences(str: string): number[] {
return [...uniqueEverseen(str)].map((char) => str.indexOf(char));
}
28 changes: 28 additions & 0 deletions typescript-challenges/code/split-on-new-chars.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/**
* Problem (taken from
* https://codegolf.stackexchange.com/questions/156170/split-string-on-first-occurrence-of-each-character):
* transform a string `str` into a list of strings `arr` such that:
* - `arr.join('') === str`
* - A new substring begins precisely when
* a previously unencountered character is encountered in the string.
*
* For example, `'aababaccabac'` should be split into `['aa', 'baba', 'ccabac']`.
*/

/** Split the given string at newly encountered characters, as per the challenge description. */
export default function splitOnNewChars(str: string): string[] {
/*
Note: V8 apparently optimizes repeated string concatenation to not be quadratic time,
so the elements of `arr` can be strings rather than arrays of characters.
*/
const arr: string[] = [];
const encounteredChars = new Set();
for (const char of str) {
if (!encounteredChars.has(char)) {
encounteredChars.add(char);
arr.push('');
}
arr[arr.length - 1] += char;
}
return arr;
}

0 comments on commit bea3bd0

Please sign in to comment.