steelbreeze/pivot

View on GitHub
docs/index.html

Summary

Maintainability
Test Coverage
<!DOCTYPE html>
<html lang="en">

<head>
    <title>steelbreeze/pivot</title>
    <style>
        @import url("https://cdn.jsdelivr.net/npm/holiday.css@0.9.8");

        th {
            text-align: left;
        }

        .Label {
            text-transform: capitalize;
        }
    </style>
</head>

<body>
    <nav>
        <ul>
            <li>
                <a href="/">steelbreeze</a>
            </li>
            <li>
                <a href="../landscape">landscape</a>
            </li>
            <li>
                <a href="../pivot">pivot</a>
            </li>
            <li>
                <a href="../state">state</a>
            </li>
        </ul>
    </nav>
    <header>
        <h1>steelbreeze/pivot</h1>
        <p>Minimalist JavaScript library written in TypeScript to pivot data by one or more dimensions.</p>
    </header>
    <main>
        <h2>Example</h2>
        <h3>Fulham Football Club men's squad at the end of the 2020/21 season</h3>
        <table id="squad"></table>
    </main>
    <html-include href="https://steelbreeze.net/components/footer.html"></html-include>
    <script src="https://steelbreeze.net/components/html-include.js"></script>
    <script src="./dist/pivot.min.js"></script>
    <script type="module">
        const squad = [
            { shirt: 1, position: 'Goalkeeper', givenName: 'Alphonse', familyName: 'Areola', country: 'France', dateOfBirth: new Date('1993-02-27') },
            { shirt: 12, position: 'Goalkeeper', givenName: 'Marek', familyName: 'Rodak', country: 'Slovakia', dateOfBirth: new Date('1996-12-13') },
            { shirt: 31, position: 'Goalkeeper', givenName: 'Fabrico', familyName: 'Agosto Ramirez', country: 'Spain', dateOfBirth: new Date('1987-12-31') },
            { shirt: 2, position: 'Defender', givenName: 'Kenny', familyName: 'Tete', country: 'Netherlands', dateOfBirth: new Date('1995-10-09') },
            { shirt: 3, position: 'Defender', givenName: 'Michael', familyName: 'Hector', country: 'Jamaica', dateOfBirth: new Date('1992-07-19') },
            { shirt: 4, position: 'Defender', givenName: 'Denis', familyName: 'Odoi', country: 'Belgium', dateOfBirth: new Date('1988-05-27') },
            { shirt: 5, position: 'Defender', givenName: 'Joachim', familyName: 'Anderson', country: 'Denmark', dateOfBirth: new Date('1996-05-31') },
            { shirt: 13, position: 'Defender', givenName: 'Tim', familyName: 'Ream', country: 'USA', dateOfBirth: new Date('1987-10-05') },
            { shirt: 16, position: 'Defender', givenName: 'Tosin', familyName: 'Abarabioyo', country: 'England', dateOfBirth: new Date('1997-09-24') },
            { shirt: 23, position: 'Defender', givenName: 'Joe', familyName: 'Bryan', country: 'England', dateOfBirth: new Date('1993-09-17') },
            { shirt: 30, position: 'Defender', givenName: 'Terence', familyName: 'Kongolo', country: 'France', dateOfBirth: new Date('1994-02-14') },
            { shirt: 33, position: 'Defender', givenName: 'Antonee', familyName: 'Robinson', country: 'USA', dateOfBirth: new Date('1997-08-08') },
            { shirt: 34, position: 'Defender', givenName: 'Ola', familyName: 'Aina', country: 'Nigeria', dateOfBirth: new Date('1996-10-08') },
            { shirt: 6, position: 'Midfielder', givenName: 'Kevin', familyName: 'McDonald', country: 'Scotland', dateOfBirth: new Date('1988-11-04') },
            { shirt: 10, position: 'Midfielder', givenName: 'Tom', familyName: 'Cairney', country: 'Scotland', dateOfBirth: new Date('1991-01-20') },
            { shirt: 15, position: 'Midfielder', givenName: 'Ruben', familyName: 'Loftus-Cheek', country: 'England', dateOfBirth: new Date('1996-01-23') },
            { shirt: 18, position: 'Midfielder', givenName: 'Mario', familyName: 'Lemina', country: 'Gabon', dateOfBirth: new Date('1993-09-01') },
            { shirt: 21, position: 'Midfielder', givenName: 'Harrison', familyName: 'Reed', country: 'England', dateOfBirth: new Date('1995-01-27') },
            { shirt: 25, position: 'Midfielder', givenName: 'Josh', familyName: 'Onomah', country: 'England', dateOfBirth: new Date('1997-04-27') },
            { shirt: 29, position: 'Midfielder', givenName: 'Andre-Frank', familyName: 'Zambo Anguissa', country: 'Cameroon', dateOfBirth: new Date('1995-11-16') },
            { shirt: 48, position: 'Midfielder', givenName: 'Fabio', familyName: 'Carvalho', country: 'England', dateOfBirth: new Date('2002-08-30') },
            { shirt: 9, position: 'Forward', givenName: 'Aleksander', familyName: 'Mitrovic', country: 'Serbia', dateOfBirth: new Date('1994-09-16') },
            { shirt: 14, position: 'Forward', givenName: 'Bobby', familyName: 'De Cordova-Reid', country: 'Jamaica', dateOfBirth: new Date('1993-02-02') },
            { shirt: 17, position: 'Forward', givenName: 'Ivan', familyName: 'Cavaleiro', country: 'Portugal', dateOfBirth: new Date('1993-10-18') },
            { shirt: 19, position: 'Forward', givenName: 'Ademola', familyName: 'Lookman', country: 'England', dateOfBirth: new Date('1997-10-20') },
            { shirt: 27, position: 'Forward', givenName: 'Josh', familyName: 'Maja', country: 'Nigeria', dateOfBirth: new Date('1998-12-27') }
        ];

        // create axes out of the dimensions derived from the squad data
        const position = ['Goalkeeper', 'Defender', 'Midfielder', 'Forward'].map(criteria('position'));
        const country = squad.map(player => player.country).filter(distinct).sort().map(criteria('country'));

        // create the pivot cube
        const cube = pivot.pivot(squad, country, position);

        // find the average age of players by position by country, select just the full name
        const result = pivot.aggregate(cube, players => players.map(player => `${player.givenName} ${player.familyName}`));

        document.getElementById("squad").innerHTML =
            `<thead>` +
            position[0].metadata.map((_, pi) => `<tr>${position[0].metadata.map(() => '<th></th>').join('')}${position.map(c => th(c.metadata[pi])).join('')}</tr>`).join('') +
            `</thead>` +
            result.map((row, i) => `<tr>${country[i].metadata.map(th).join('')}${row.map(cell => td(cell || '', '')).join('')}</tr>`).join('');

        // generate a header cell in a table
        function th(data) {

            return `<th${data.key ? ` class="${data.key}"` : ``}>${data.value}</th>`;
        }

        // generate a cell in a table
        function td(t, c) {
            return `<td${c ? ` class="${c}"` : ``}>${t.join(', ')}</td>`;
        }

        // create criteria with little metadata
        function criteria(key) {
            return value => Object.assign(
                row => row[key] === value,
                {
                    metadata: [
                        //                        { key: 'key', value: key },
                        { key, value }
                    ]
                });
        }

        // function to pass into Array.prototype.filter to return unique values.
        function distinct(value, index, source) {
            return source.indexOf(value) === index;
        }

    </script>
</body>

</html>