Skip to content

List Set Operations — C++ source

Compute the union, intersection, difference, or symmetric difference of two newline-separated lists. Deduped results with optional trim and case-insensitive matching. Runs entirely in your browser, with a shareable link.

This is the C++ implementation — the same logic the interactive tool runs, in a shareable, citable form.

// list-set-ops — union / intersection / difference / symmetric difference of
// two newline-separated lists. C++17 port (canonical TS:
// src/lib/list-set-ops.ts; Go twin: cli/list-set-ops). std::unordered_set is
// the membership set; trim defaults on like the TS `trim !== false`.
#include <cctype>
#include <iostream>
#include <sstream>
#include <string>
#include <unordered_set>
#include <vector>

struct SetOptions {
    bool caseInsensitive = false;
    bool trim = true; // the TS default (omitted -> trim on)
};

// Folded membership key: lowercase copy when case-insensitive, else the item.
std::string key(const std::string& s, const SetOptions& o) {
    if (!o.caseInsensitive) return s;
    std::string k = s;
    for (char& c : k) c = static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
    return k;
}
// Split a newline list into a deduped vector (first-seen casing kept).
std::vector<std::string> parseList(const std::string& input, const SetOptions& o) {
    std::vector<std::string> out;
    if (input.empty()) return out; // empty input = no items
    std::unordered_set<std::string> seen;
    std::istringstream in(input);
    std::string item;
    while (std::getline(in, item)) {
        if (o.trim) { // trim whitespace, drop empty lines
            const char* ws = " \t\r";
            size_t b = item.find_first_not_of(ws), e = item.find_last_not_of(ws);
            item = b == std::string::npos ? "" : item.substr(b, e - b + 1);
            if (item.empty()) continue;
        }
        if (seen.insert(key(item, o)).second) out.push_back(item);
    }
    return out;
}

// Compute a set op over two lists; the result is "\n"-joined.
std::string setOps(const std::string& a, const std::string& b, const std::string& op,
                   SetOptions o = SetOptions()) {
    const auto listA = parseList(a, o), listB = parseList(b, o);
    std::unordered_set<std::string> keysA, keysB;
    for (const auto& s : listA) keysA.insert(key(s, o));
    for (const auto& s : listB) keysB.insert(key(s, o));

    std::vector<std::string> out;
    if (op == "union") { // every unique item, a then b
        std::unordered_set<std::string> seen;
        for (const auto& s : listA) if (seen.insert(key(s, o)).second) out.push_back(s);
        for (const auto& s : listB) if (seen.insert(key(s, o)).second) out.push_back(s);
    } else if (op == "intersect") { // items present in both (a's order/casing)
        for (const auto& s : listA) if (keysB.count(key(s, o))) out.push_back(s);
    } else if (op == "difference") { // items in a not present in b
        for (const auto& s : listA) if (!keysB.count(key(s, o))) out.push_back(s);
    } else if (op == "symmetricDifference") { // exactly one list, a-only then b-only
        for (const auto& s : listA) if (!keysB.count(key(s, o))) out.push_back(s);
        for (const auto& s : listB) if (!keysA.count(key(s, o))) out.push_back(s);
    } // unknown op -> ""
    std::string joined;
    for (size_t i = 0; i < out.size(); i++) {
        if (i) joined += '\n';
        joined += out[i];
    }
    return joined;
}

int main() {
    const std::string A = "apple\nbanana\ncherry", B = "banana\ncherry\ndate";
    std::cout << setOps(A, B, "union") << "\n";              // apple\nbanana\ncherry\ndate
    std::cout << setOps(A, B, "intersect") << "\n";          // banana\ncherry
    std::cout << setOps(A, B, "difference") << "\n";         // apple
    std::cout << setOps(A, B, "symmetricDifference") << "\n"; // apple\ndate
    // case-folded compare, first-seen original casing emitted
    std::cout << setOps("HELLO", "hello", "intersect", SetOptions{true}) << "\n"; // HELLO
}

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 →