Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement
Sign In

grid-sort

Package Overview
Dependencies
Maintainers
1
Versions
7
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

grid-sort - npm Package Compare versions

Comparing version
2.1.0
to
2.1.1
+1
-1
package.json
{
"name": "grid-sort",
"version": "2.1.0",
"version": "2.1.1",
"description": "Sort objects in a two dimensional array to compose grids based on a condition",

@@ -5,0 +5,0 @@ "main": "./lib/index.js",

Sorry, the diff of this file is not supported yet

name: CI
on: [push, pull_request]
jobs:
test:
name: Test
runs-on: ubuntu-20.04
timeout-minutes: 3
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
node-version: 14
- name: Dependencies
run: yarn install --frozen-lockfile
- name: Test with coverage
run: |
yarn check-coverage
yarn report-coverage
- uses: codecov/codecov-action@v2
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: ./coverage.lcov
name: Codecov
{"version":3,"file":"gridSort.js","sourceRoot":"","sources":["../src/gridSort.ts"],"names":[],"mappings":"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"}
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC"}
export type Bucket<T> = T[];
export type Buckets<T> = { [key: number]: Bucket<T> };
export type Accessor<T extends object> = (item: T) => number;
const keysOf = <T extends object>(object: T) =>
Object.keys(object) as (keyof typeof object)[];
const itemsInBucket = <T extends unknown>(
buckets: Buckets<T>,
key: keyof Buckets<T>
) => buckets[key]?.length || 0 > 0;
const rowQuantity = <T extends object | number>(
row: T[],
accessor?: T extends object ? Accessor<T> : undefined
) =>
row.reduce((acc, item) => {
if (accessor && typeof item === "object") {
const value = accessor(item);
return acc + value;
}
return acc + Number(item);
}, 0);
export function getSortedKeys<T>(buckets: Buckets<T>) {
return keysOf(buckets).sort((a, z) => z - a);
}
export function calculateItemsLeft<T>(buckets: Buckets<T>, columns: number, accessor?: T extends object ? Accessor<T> : undefined) {
return Object.values(buckets)
.reduce((acc, bucket) => {
const bucketQuantity = bucket.map(value => {
if (accessor && typeof value === 'object') {
return accessor(value);
}
return value;
}).filter(value => value <= columns).length;
return acc + bucketQuantity;
}, 0);
}
export function rowsAreEqual<T extends object | number>(
rowA: T[],
rowB: T[],
accessor?: T extends object ? Accessor<T> : undefined
) {
return rowA.every((value, index) => {
let itemA = value;
let itemB = rowB[index] as T;
if (accessor && typeof itemA === "object" && typeof itemB === "object") {
return accessor(itemA) === accessor(itemB);
}
return itemA === itemB;
});
}
export function groupItemsByValue<T extends object | number>(
items: T[],
accessor?: T extends object ? Accessor<T> : undefined
) {
const groups: Buckets<T> = {};
for (const item of items) {
let key: number;
if (accessor && typeof item === "object") {
key = accessor(item);
} else {
key = Number(item);
}
const group = groups[key];
if (group && Array.isArray(group)) {
group.push(item);
} else {
groups[key] = [item];
}
}
return groups;
}
export function getFillableItem<T>(buckets: Buckets<T>, maxKey: number) {
let currentBiggestKey = 0;
const sortedKeys = getSortedKeys(buckets);
for (const key of sortedKeys) {
if (
itemsInBucket(buckets, key) &&
key > currentBiggestKey &&
key <= maxKey
) {
currentBiggestKey = key;
}
}
return currentBiggestKey;
}
export function buildGridFromBuckets<T extends object | number>({
buckets,
columns,
accessor,
}: {
buckets: Buckets<T>;
columns: number;
accessor?: T extends object ? Accessor<T> : undefined;
}) {
const grid: T[][] = [];
if (columns <= 0) return grid;
let itemsLeft: number;
while ((itemsLeft = calculateItemsLeft(buckets, columns, accessor)) > 0) {
const row: T[] = [];
let key: number;
while (
(key = getFillableItem(buckets, columns - rowQuantity(row, accessor))) &&
rowQuantity(row, accessor) < columns &&
itemsLeft >= 1
) {
const item = buckets[key]?.shift();
if (item) {
row.push(item);
}
}
// randomize current row if previous row has same layout
const previousRow = grid[grid.length - 1];
if (previousRow && rowsAreEqual(row, previousRow, accessor)) {
row.reverse();
}
grid.push(row);
}
return grid;
}
export function gridSort<T extends object | number>({
items,
columns = 4,
accessor,
}: {
items: T[];
columns?: number;
accessor?: T extends object ? Accessor<T> : never;
}) {
const buckets = groupItemsByValue(items, accessor);
return buildGridFromBuckets({ buckets, columns, accessor });
}
export { gridSort } from "./gridSort.js";

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

import { test, suite } from "uvu";
import * as assert from "uvu/assert";
import {
buildGridFromBuckets,
getFillableItem,
getSortedKeys,
groupItemsByValue,
rowsAreEqual,
} from "../src/gridSort.js";
test("sorts object keys numerically in descending order", () => {
const buckets = { 1: [1, 1, 1], 2: [2, 2], 3: [3, 3] };
assert.equal(getSortedKeys(buckets), [3, 2, 1].map(String));
});
test("gets best candidate for filling a column", () => {
const bucketsA = { 1: [1, 1, 1], 2: [2, 2], 3: [3, 3] };
assert.equal(getFillableItem(bucketsA, 4), "3");
const bucketsB = { 1: [1, 1, 1], 2: [2, 2], 3: [3, 3] };
assert.equal(getFillableItem(bucketsB, 1), "1");
const bucketsC = { 1: [1, 1, 1], 2: [2, 2], 3: [3, 3] };
assert.equal(getFillableItem(bucketsC, 2), "2");
const bucketsD = { 1: [1, 1, 1], 2: [2, 2], 3: [] };
assert.equal(getFillableItem(bucketsD, 4), "2");
const bucketsE = { 1: [1, 1, 1], 2: [], 3: [] };
assert.equal(getFillableItem(bucketsE, 4), "1");
const bucketsF = { 1: [1, 1, 1], 2: [2, 2], 3: [3, 3] };
assert.equal(getFillableItem(bucketsF, 2), "2");
const bucketsG = { 1: [1, 1, 1], 2: [2, 2], 3: [3, 3] };
assert.equal(getFillableItem(bucketsG, 1), "1");
});
test("rows equality", () => {
const [rowA, rowB] = [
[
{ id: "1", ratio: 3 },
{ id: "2", ratio: 1 },
],
[
{ id: "8", ratio: 3 },
{ id: "5", ratio: 1 },
],
];
const accessor = (item: typeof rowA[number]) => item.ratio;
assert.ok(rowsAreEqual(rowA, rowB, accessor));
assert.not.ok(rowsAreEqual(rowA, rowB.reverse(), accessor));
});
test("it sorts bucket of numbers", () => {
const buckets = groupItemsByValue([1, 1, 1, 1, 1, 2, 2, 3, 3]);
const sorted = buildGridFromBuckets({
buckets,
columns: 4,
accessor: undefined,
});
const expected = [
[3, 1],
[1, 3],
[2, 2],
[1, 1, 1],
];
assert.equal(sorted, expected);
});
test("it builds buckets from array of items", () => {
const items = [
{ id: "1", ratio: 3 },
{ id: "2", ratio: 1 },
{ id: "3", ratio: 2 },
{ id: "4", ratio: 2 },
{ id: "5", ratio: 1 },
{ id: "7", ratio: 1 },
{ id: "8", ratio: 3 },
];
const itemsByValue = groupItemsByValue(items, (item) => item.ratio);
assert.equal(itemsByValue, {
1: [
{ id: "2", ratio: 1 },
{ id: "5", ratio: 1 },
{ id: "7", ratio: 1 },
],
2: [
{ id: "3", ratio: 2 },
{ id: "4", ratio: 2 },
],
3: [
{ id: "1", ratio: 3 },
{ id: "8", ratio: 3 },
],
});
});
test("it sorts bucket of objects", () => {
const items = [
{ id: "1", ratio: 3 },
{ id: "2", ratio: 1 },
{ id: "3", ratio: 2 },
{ id: "4", ratio: 2 },
{ id: "5", ratio: 1 },
{ id: "7", ratio: 1 },
{ id: "8", ratio: 3 },
];
const accessor = (item: typeof items[number]) => item.ratio;
const itemsByValue = groupItemsByValue(items, accessor);
const sorted = buildGridFromBuckets({
buckets: itemsByValue,
columns: 4,
accessor,
});
const expected = [
[
{ id: "1", ratio: 3 },
{ id: "2", ratio: 1 },
],
[
{ id: "5", ratio: 1 },
{ id: "8", ratio: 3 },
],
[
{ id: "3", ratio: 2 },
{ id: "4", ratio: 2 },
],
[{ id: "7", ratio: 1 }],
];
assert.equal(sorted, expected);
});
test.run();
const ColumnSuite = suite('Columns Suite');
ColumnSuite("it sorts bucket of objects with more columns", () => {
const items = [
{ id: "1", ratio: 3 },
{ id: "2", ratio: 1 },
{ id: "3", ratio: 2 },
{ id: "4", ratio: 2 },
{ id: "5", ratio: 1 },
{ id: "7", ratio: 1 },
{ id: "8", ratio: 3 },
];
const accessor = (item: typeof items[number]) => item.ratio;
const itemsByValue = groupItemsByValue(items, accessor);
const sorted = buildGridFromBuckets({
buckets: itemsByValue,
columns: 5,
accessor,
});
const expected = [
[
{ id: "1", ratio: 3 },
{ id: "3", ratio: 2 },
],
[
{ id: "4", ratio: 2 },
{ id: "8", ratio: 3 },
],
[
{ id: "2", ratio: 1 },
{ id: "5", ratio: 1 },
{ id: "7", ratio: 1 },
],
];
assert.equal(sorted, expected);
});
ColumnSuite("it sorts bucket of objects with less columns than items", () => {
const items = [
{ id: "1", ratio: 3 },
{ id: "2", ratio: 1 },
{ id: "3", ratio: 2 },
{ id: "4", ratio: 2 },
{ id: "5", ratio: 1 },
{ id: "7", ratio: 1 },
{ id: "8", ratio: 3 },
];
const accessor = (item: typeof items[number]) => item.ratio;
const itemsByValue = groupItemsByValue(items, accessor);
const sorted = buildGridFromBuckets({
buckets: itemsByValue,
columns: 3,
accessor,
});
const expected = [
[
{ id: "1", ratio: 3 },
],
[
{ id: "8", ratio: 3 },
],
[
{ id: "3", ratio: 2 },
{ id: "2", ratio: 1 },
],
[
{ id: "5", ratio: 1 },
{ id: "4", ratio: 2 },
],
[
{ id: "7", ratio: 1 },
]
];
assert.equal(sorted, expected);
});
ColumnSuite("it sorts bucket of objects with less columns than biggest object", () => {
const items = [
{ id: "1", ratio: 3 },
{ id: "2", ratio: 1 },
{ id: "3", ratio: 2 },
{ id: "4", ratio: 2 },
{ id: "5", ratio: 1 },
{ id: "7", ratio: 1 },
{ id: "8", ratio: 3 },
];
const accessor = (item: typeof items[number]) => item.ratio;
const itemsByValue = groupItemsByValue(items, accessor);
const sorted = buildGridFromBuckets({
buckets: itemsByValue,
columns: 2,
accessor,
});
const expected = [
[
{ id: "3", ratio: 2 },
],
[
{ id: "4", ratio: 2 },
],
[
{ id: "2", ratio: 1 },
{ id: "5", ratio: 1 },
],
[
{ id: "7", ratio: 1 },
]
];
assert.equal(sorted, expected);
});
ColumnSuite("it sorts bucket of objects with a single column", () => {
const items = [
{ id: "1", ratio: 3 },
{ id: "2", ratio: 1 },
{ id: "3", ratio: 2 },
{ id: "4", ratio: 2 },
{ id: "5", ratio: 1 },
{ id: "7", ratio: 1 },
{ id: "8", ratio: 3 },
];
const accessor = (item: typeof items[number]) => item.ratio;
const itemsByValue = groupItemsByValue(items, accessor);
const sorted = buildGridFromBuckets({
buckets: itemsByValue,
columns: 1,
accessor,
});
const expected = [
[
{ id: "2", ratio: 1 },
],
[
{ id: "5", ratio: 1 },
],
[
{ id: "7", ratio: 1 },
]
];
assert.equal(sorted, expected);
});
ColumnSuite("it sorts bucket of objects with no columns", () => {
const items = [
{ id: "1", ratio: 3 },
{ id: "2", ratio: 1 },
{ id: "3", ratio: 2 },
{ id: "4", ratio: 2 },
{ id: "5", ratio: 1 },
{ id: "7", ratio: 1 },
{ id: "8", ratio: 3 },
];
const accessor = (item: typeof items[number]) => item.ratio;
const itemsByValue = groupItemsByValue(items, accessor);
const sorted = buildGridFromBuckets({
buckets: itemsByValue,
columns: 0,
accessor,
});
assert.equal(sorted, []);
});
ColumnSuite.run();
import { test } from "uvu";
import * as assert from "uvu/assert";
import { gridSort } from "../src/index.js";
test("it sorts an array of items into a grid", () => {
const items = [
{ id: "1", ratio: 3 },
{ id: "2", ratio: 1 },
{ id: "3", ratio: 2 },
{ id: "4", ratio: 2 },
{ id: "5", ratio: 1 },
{ id: "7", ratio: 1 },
{ id: "8", ratio: 3 },
];
const accessor = (item: typeof items[number]) => item.ratio;
const sorted = gridSort({
items,
columns: 5,
accessor,
});
const expected = [
[
{ id: "1", ratio: 3 },
{ id: "3", ratio: 2 },
],
[
{ id: "4", ratio: 2 },
{ id: "8", ratio: 3 },
],
[
{ id: "2", ratio: 1 },
{ id: "5", ratio: 1 },
{ id: "7", ratio: 1 },
],
];
assert.equal(sorted, expected);
});
test.run();
{
"compilerOptions": {
"declaration": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"lib": ["ESNext"],
"module": "ES2020",
"moduleResolution": "node",
"noFallthroughCasesInSwitch": true,
"noImplicitAny": true,
"noImplicitOverride": true,
"noImplicitReturns": true,
"noImplicitThis": true,
"noPropertyAccessFromIndexSignature": true,
"noUncheckedIndexedAccess": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"outDir": "./lib",
"strict": true,
"strictBindCallApply": true,
"strictFunctionTypes": true,
"strictNullChecks": true,
"strictPropertyInitialization": true,
"skipLibCheck": true,
"target": "ES2015",
"sourceMap": true
},
"include": ["src"]
}