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. C99 port (canonical TS:
* src/lib/list-set-ops.ts; Go twin: cli/list-set-ops). C has no stdlib set
* or dynamic array, so membership is a linear scan over fixed caps (fine at
* tool scale) and the join is a malloc'd buffer. ASCII-only trim/fold. */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef enum { UNION, INTERSECT, DIFFERENCE, SYMDIFF } SetOp;
/* Membership-key compare: exact, or ASCII case-folded when ci is set. */
static int key_eq(const char *a, const char *b, int ci) {
while (*a && *b) {
char x = *a++, y = *b++;
if (ci) { if (x >= 'A' && x <= 'Z') x += 32; if (y >= 'A' && y <= 'Z') y += 32; }
if (x != y) return 0;
}
return *a == *b;
}
static int has_key(char **list, int n, const char *item, int ci) {
for (int i = 0; i < n; i++) if (key_eq(list[i], item, ci)) return 1;
return 0;
}
/* Split a newline list into a deduped item list (first-seen casing kept);
* trim drops ASCII-whitespace edges and empty lines (the TS default). */
static int parse_list(const char *in, int ci, int trim, char **out) {
int n = 0;
const char *p = in;
if (!*in) return 0; /* empty input = no items */
while (1) {
const char *eol = strchr(p, '\n'), *end = eol ? eol : p + strlen(p);
const char *s = p, *t = end;
if (trim) {
while (s < end && (*s == ' ' || *s == '\t' || *s == '\r')) s++;
while (t > s && (t[-1] == ' ' || t[-1] == '\t' || t[-1] == '\r')) t--;
}
size_t len = (size_t)(t - s);
if (!trim || len > 0) {
char *item = malloc(len + 1);
if (!item) break;
memcpy(item, s, len), item[len] = 0;
if (has_key(out, n, item, ci)) free(item); else out[n++] = item;
}
if (!eol) break;
p = eol + 1;
}
return n;
}
/* Compute a set op over two lists; returns a malloc'd "\n"-joined string. */
static char *set_ops(const char *a, const char *b, SetOp op, int ci, int trim) {
char *la[256], *lb[256], *res[512];
int na = parse_list(a, ci, trim, la), nb = parse_list(b, ci, trim, lb), n = 0, i;
if (op == UNION) { /* every unique item, a then b */
for (i = 0; i < na; i++) res[n++] = la[i];
for (i = 0; i < nb; i++) if (!has_key(la, na, lb[i], ci)) res[n++] = lb[i];
} else if (op == INTERSECT) {
for (i = 0; i < na; i++) if (has_key(lb, nb, la[i], ci)) res[n++] = la[i];
} else if (op == DIFFERENCE) {
for (i = 0; i < na; i++) if (!has_key(lb, nb, la[i], ci)) res[n++] = la[i];
} else if (op == SYMDIFF) { /* a-only then b-only */
for (i = 0; i < na; i++) if (!has_key(lb, nb, la[i], ci)) res[n++] = la[i];
for (i = 0; i < nb; i++) if (!has_key(la, na, lb[i], ci)) res[n++] = lb[i];
} /* unknown op -> "" */
size_t cap = 1;
for (i = 0; i < n; i++) cap += strlen(res[i]) + 1;
char *out = malloc(cap), *w = out;
for (i = 0; i < n; i++) w += sprintf(w, i ? "\n%s" : "%s", res[i]);
return out;
}
int main(void) {
const char *A = "apple\nbanana\ncherry", *B = "banana\ncherry\ndate";
puts(set_ops(A, B, UNION, 0, 1)); /* apple\nbanana\ncherry\ndate */
puts(set_ops(A, B, INTERSECT, 0, 1)); /* banana\ncherry */
puts(set_ops(A, B, DIFFERENCE, 0, 1)); /* apple */
puts(set_ops(A, B, SYMDIFF, 0, 1)); /* apple\ndate */
puts(set_ops("HELLO", "hello", INTERSECT, 1, 1)); /* HELLO — case-folded key */
return 0;
}
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 →