Skip to content

Instantly share code, notes, and snippets.

@fedemartinm
Created February 3, 2023 07:21
Show Gist options
  • Save fedemartinm/02e044e78501c2b342f1cac09b0715a4 to your computer and use it in GitHub Desktop.
Save fedemartinm/02e044e78501c2b342f1cac09b0715a4 to your computer and use it in GitHub Desktop.
A compact tool to create a series of objects from a specific template, iterating through the different values for each property

Data generator utility

I'm writing a minifier for lexical nodes. Writing unit tests and after taking a quick look at the options for generating test data (factory-girl, faker.js, casual.js, etc) I saw that there wasn't one that would allow me to quickly define different values per property and generate a list of objects that use those values.

I am sharing a small utility to generate a list of objects from a shape, that iterates through the different possibilities for each property.

type Shape<T> = {
  [key in keyof T]: any;
};

function* oneOf(...args: any[]) {
  let index = 0;
  while (index < args.length) {
    if (index < args.length - 1) {
      yield args[index];
    } else {
      return args[index];
    }
    index++;
  }
}

function isGenerator(obj: any): obj is Generator {
  return (
    !Array.isArray(obj) &&
    typeof obj === "object" &&
    typeof obj[Symbol.iterator] === "function"
  );
}

function create<T>(shape: Shape<T>): T[] {
  const values: T[] = [];
  let shouldContinue = true;
  let item: any = {};

  while (shouldContinue) {
    shouldContinue = false;

    Object.entries(shape).forEach(([key, def]: [string, any]) => {
      if (isGenerator(def)) {
        const { value, done } = def.next();
        if (done === false) {
          shouldContinue = true;
          item[key] = value;
        } else if (typeof value !== "undefined") {
          item[key] = value;
        }
      } else {
        item[key] = def;
      }
    });
    values.push(<T>{ ...item });
  }

  return values;
}

Example

const paragraphs = create<SerializedParagraphNode>({
  children: [],
  direction: oneOf("ltr", "rtl", null),
  format: oneOf("left", "start", "center", "right", "end", "justify", ""),
  indent: oneOf(0, 1, 2),
  type: "paragraph",
  version: 1,
});

/* output: 
[{
  children: [],
  direction: 'ltr',
  format: 'left',
  indent: 0,
  type: 'paragraph',
  version: 1
},
{
  children: [],
  direction: 'rtl',
  format: 'start',
  indent: 1,
  type: 'paragraph',
  version: 1
},
{
  children: [],
  direction: null,
  format: 'center',
  indent: 2,
  type: 'paragraph',
  version: 1
},
{
  children: [],
  direction: null,
  format: 'right',
  indent: 2,
  type: 'paragraph',
  version: 1
},
{
  children: [],
  direction: null,
  format: 'end',
  indent: 2,
  type: 'paragraph',
  version: 1
},
{
  children: [],
  direction: null,
  format: 'justify',
  indent: 2,
  type: 'paragraph',
  version: 1
},
{
  children: [],
  direction: null,
  format: '',
  indent: 2,
  type: 'paragraph',
  version: 1
}]
*/
@fedemartinm
Copy link
Author

Here's the JavaScript version:

function* oneOf(...args) {
    let index = 0;
    while (index < args.length) {
        if (index < args.length - 1) {
            yield args[index];
        }
        else {
            return args[index];
        }
        index++;
    }
}
function isGenerator(obj) {
    return (!Array.isArray(obj) &&
        typeof obj === "object" &&
        typeof obj[Symbol.iterator] === "function");
}
function create(shape) {
    const values = [];
    let shouldContinue = true;
    let item = {};
    while (shouldContinue) {
        shouldContinue = false;
        Object.entries(shape).forEach(([key, def]) => {
            if (isGenerator(def)) {
                const { value, done } = def.next();
                if (done === false) {
                    shouldContinue = true;
                    item[key] = value;
                }
                else if (typeof value !== "undefined") {
                    item[key] = value;
                }
            }
            else {
                item[key] = def;
            }
        });
        values.push(Object.assign({}, item));
    }
    return values;
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment