Creating a 9 box in retool

Sure you can!

You need to grade/calibrate each individual on the x and y attributes, put those calibrations into an array with appropriate bins, and then you can allocate individuals into the appropriate bins and show them in a series of containers in a grid view.

Here's a silly example to get you started:

Approach:
Having no idea what you will actually be grading on, I decided to grade a random list of names (thanks to ChatGPT, with some edits to make the data interesting) on the count of consonants and vowels in each name.

[
    { name: "Alisonia" },
    { name: "Bobby" },
    { name: "Charlie" },
    { name: "David" },
    { name: "Emmaline" },
    { name: "falalalalaaaa" },
    { name: "Gracious" },
    { name: "Henry" },
    { name: "Fooooooo" },
    { name: "Jackson" },
    { name: "Katherine" },
    { name: "Liam" },
    { name: "Mia" },
    { name: "Noah" },
    { name: "Olivia" },
    { name: "Peter" },
    { name: "Quinnick" },
    { name: "Rachel" }
]

From there, it's a matter of math + JavaScript to get the counts, figure out the bin sizes to use, and assign the names to the appropriate bin:

// Array of names from the jsonExplorer component
const names = jsonExplorer1.value;
// Functions to count vowels and consonants in a string
function countVowels(str) {
    return str.match(/[aeiou]/gi).length;
}
function countConsonants(str) {
    return str.match(/[^aeiou]/gi).length;
}
// Find minimum and maximum counts of vowels and consonants
let minVowels = Infinity;
let maxVowels = -Infinity;
let minConsonants = Infinity;
let maxConsonants = -Infinity;
names.forEach(nameObj => {
    const numVowels = countVowels(nameObj.name);
    const numConsonants = countConsonants(nameObj.name);
    minVowels = Math.min(minVowels, numVowels);
    maxVowels = Math.max(maxVowels, numVowels);
    minConsonants = Math.min(minConsonants, numConsonants);
    maxConsonants = Math.max(maxConsonants, numConsonants);
});
// Define bin ranges for 3 by 3
const vowelBinWidth = Math.ceil((maxVowels - minVowels + 1) / 3);
const consonantBinWidth = Math.ceil((maxConsonants - minConsonants + 1) / 3);
const vowelBins = [
    { min: minVowels,
      max: minVowels + vowelBinWidth - 1,
      definition: "Low" },
    { min: minVowels + vowelBinWidth,
      max: minVowels + 2 * vowelBinWidth - 1,
      definition: "Medium" },
    { min: minVowels + 2 * vowelBinWidth,
      max: Infinity,
      definition: "High" }
];
const consonantBins = [
    { min: minConsonants,
      max: minConsonants + consonantBinWidth - 1,
      definition: "Low" },
    { min: minConsonants + consonantBinWidth,
      max: minConsonants + 2 * consonantBinWidth - 1,
      definition: "Medium" },
    { min: minConsonants + 2 * consonantBinWidth,
      max: Infinity,
      definition: "High" }
];
// Empty array to store names in each bin
const bins = [];
// Initialize bins with a definition of each
for (let i = 0; i < vowelBins.length; i++) {
    for (let j = 0; j < consonantBins.length; j++) {
        bins.push({
            vowelRange: vowelBins[i],
            consonantRange: consonantBins[j],
            definition: `${vowelBins[i].definition} Vowels,\n ${consonantBins[j].definition} Consonants`,
            names: []
        });
    }
}
// Iterate through names and assign them to bins
names.forEach(nameObj => {
    const numVowels = countVowels(nameObj.name);
    const numConsonants = countConsonants(nameObj.name);
    // Find the appropriate bin
    const bin = bins.find(bin =>
        numVowels >= bin.vowelRange.min && numVowels <= bin.vowelRange.max &&
        numConsonants >= bin.consonantRange.min && numConsonants <= bin.consonantRange.max
    );
    // Add the name to the bin
    if (bin) {
        bin.names.push(nameObj.name);
    }
});

return bins

I used the JS output as the data source for the grid component, added {{item.definition}} as the title of the containers, and added a link list as a component to the containers with the data source for the link list of {{item.names}}.

Whew! That was fun :slight_smile:

7 Likes