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 →