List Converter — JavaScript source
Transform a list between separators (newline, comma, space, pipe, semicolon, tab) with trim, dedupe, sort, and empty-removal options. Runs entirely in your browser, with a shareable link.
This is the JavaScript implementation — the same logic the interactive tool runs, in a shareable, citable form.
/**
* list-converter - convert lists between separators (newline/comma/space/pipe/...).
*
* Language: JavaScript (ES2020+, runs unmodified in Node 16+ and modern browsers)
* Source: CosmoDev polyglot showcase port of the List Converter tool, ported
* from cli/list-converter/list-converter.go (the Go CLI twin),
* which is itself the lock-step mirror of src/lib/list-converter.ts
* (the canonical TypeScript implementation).
* License: display source - part of CosmoDev's polyglot tool pages.
*
* Design goals:
* - Pure + deterministic; never throws.
* - Functionally equivalent to the Go twin: same inputs -> same outputs.
* - Self-contained: stdlib only (no npm dependencies).
*
* Pipeline: split on the "from" separator -> (trim each item) -> (drop empties)
* -> (dedup, optionally case-insensitive, first occurrence wins) -> (stable
* sort, optionally case-insensitive) -> join with the "to" separator. The
* Separator set and options mirror the Go types exactly; defaults are
* from=newline, to=comma (the TS resolveSep fallbacks).
*/
'use strict';
/**
* The built-in separators, keyed by the TS `Separator` name. Mirrors Go's
* `sepString` switch - the value is the literal split/join glue.
*/
const SEP_CHAR = Object.freeze({
newline: '\n',
comma: ',',
space: ' ',
pipe: '|',
semicolon: ';',
tab: '\t',
});
/**
* Resolve a separator name (or a literal custom string) to its glue. Unknown
* keys fall through as a literal separator, matching the TS `resolveSep`
* behavior; the enum-only Go twin simply never exercises that path.
* @param {string} [name]
* @param {string} fallback
* @returns {string}
*/
function resolveSep(name, fallback) {
if (name === undefined) return SEP_CHAR[fallback];
if (Object.prototype.hasOwnProperty.call(SEP_CHAR, name)) return SEP_CHAR[name];
return name;
}
/**
* Options shape. Keys are all optional.
* @typedef {Object} ListOptions
* @property {('newline'|'comma'|'space'|'pipe'|'semicolon'|'tab')|string} [from] Source separator. Defaults to 'newline'.
* @property {('newline'|'comma'|'space'|'pipe'|'semicolon'|'tab')|string} [to] Target separator. Defaults to 'comma'.
* @property {boolean} [trim] Trim each split item (before removeEmpty). Defaults to false.
* @property {boolean} [removeEmpty] Drop items equal to ''. Defaults to false.
* @property {boolean} [unique] Keep the first occurrence of each item. Defaults to false.
* @property {boolean} [sort] Stable-sort ascending. Defaults to false.
* @property {boolean} [caseInsensitive] Lowercase keys for unique/sort; keep original casing in output. Defaults to false.
*/
/**
* Convert a list between separators. Never throws; an empty `input` simply
* yields a one-item list joined back together (mirroring Go's
* `strings.Split("", sep) == [""]`).
*
* @param {string} input
* @param {ListOptions} [options={}]
* @returns {string}
*/
function convertList(input, options = {}) {
const fromCh = resolveSep(options.from, 'newline');
const toCh = resolveSep(options.to, 'comma');
let items = input.split(fromCh);
if (options.trim) items = items.map((s) => s.trim());
if (options.removeEmpty) items = items.filter((s) => s !== '');
if (options.unique) {
// Keyed by the (optionally lowercased) value; the first-seen item
// survives with its original casing. Set gives O(1) membership.
const seen = new Set();
items = items.filter((s) => {
const key = options.caseInsensitive ? s.toLowerCase() : s;
if (seen.has(key)) return false;
seen.add(key);
return true;
});
}
if (options.sort) {
// Array.prototype.sort is stable in ES2019+. We lowercase only for the
// comparison when caseInsensitive is set, leaving the stored values as-is.
items = [...items].sort((a, b) => {
const ka = options.caseInsensitive ? a.toLowerCase() : a;
const kb = options.caseInsensitive ? b.toLowerCase() : b;
return ka < kb ? -1 : ka > kb ? 1 : 0;
});
}
return items.join(toCh);
}
/**
* Convenience wrapper using default options - the common "newline list to a
* comma list" case (matches the tool's default From/To).
*
* @param {string} input
* @returns {string}
*/
function convertListDefault(input) {
return convertList(input, {});
}
// CommonJS export so the file is consumable from Node without a build step,
// while staying dependency-free and framework-agnostic.
module.exports = { convertList, convertListDefault, resolveSep, SEP_CHAR };
Also available in 13 other languages
Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →