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. 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 →