Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 20 additions & 20 deletions public/locales/en/list.json
Original file line number Diff line number Diff line change
Expand Up @@ -113,36 +113,36 @@
"trimItemsDescription": "Remove leading and trailing spaces before comparing items.",
"uniqueItemOptions": "Unique Item Options"
},
"group": {
"chunk": {
"deleteEmptyItems": "Delete Empty Items",
"deleteEmptyItemsDescription": "Ignore empty items and don't include them in the groups.",
"description": "World's simplest browser-based utility for grouping list items. Input your list and specify grouping criteria to organize items into logical groups. Perfect for categorizing data, organizing information, or creating structured lists. Supports custom separators and various grouping options.",
"deleteEmptyItemsDescription": "Ignore empty items and exclude them from the result.",
"description": "Split a list into consecutive chunks of a fixed size. This tool separates input items, chunks them sequentially, optionally pads incomplete chunks, and formats the output using custom separators and wrappers.",
"emptyItemsAndPadding": "Empty Items and Padding",
"groupNumberDescription": "Number of items in a group",
"groupSeparatorDescription": "Group separator character",
"groupSizeAndSeparators": "Group Size and Separators",
"groupNumberDescription": "Number of items per group",
"groupSeparatorDescription": "Separator placed between groups",
"groupSizeAndSeparators": "Group Size and Output Format",
"inputItemSeparator": "Input Item Separator",
"inputTitle": "Input list",
"itemSeparatorDescription": "Item separator character",
"leftWrapDescription": "Group's left wrap symbol.",
"padNonFullGroups": "Pad Non-full Groups",
"padNonFullGroupsDescription": "Fill non-full groups with a custom item (enter below).",
"paddingCharDescription": "Use this character or item to pad non-full groups.",
"resultTitle": "Grouped items",
"rightWrapDescription": "Group's right wrap symbol.",
"shortDescription": "Group list items by common properties",
"itemSeparatorDescription": "Separator placed between items inside a group",
"leftWrapDescription": "Symbol added before each group",
"padNonFullGroups": "Pad Incomplete Groups",
"padNonFullGroupsDescription": "Fill the last group if it has fewer items than required.",
"paddingCharDescription": "Character or value used to pad incomplete groups.",
"resultTitle": "Grouped output",
"rightWrapDescription": "Symbol added after each group",
"shortDescription": "Split a list into fixed-size chunks",
"splitOperators": {
"regex": {
"description": "Delimit input list items with a regular expression.",
"title": "Use a Regex for Splitting"
"description": "Split input items using a regular expression.",
"title": "Use Regex Splitting"
},
"symbol": {
"description": "Delimit input list items with a character.",
"title": "Use a Symbol for Splitting"
"description": "Split input items using a single character.",
"title": "Use Symbol Splitting"
}
},
"splitSeparatorDescription": "Set a delimiting symbol or regular expression.",
"title": "Group"
"splitSeparatorDescription": "Delimiter symbol or regular expression used to split input items.",
"title": "Chunk List"
},
"reverse": {
"description": "This is a super simple browser-based application prints all list items in reverse. The input items can be separated by any symbol and you can also change the separator of the reversed list items.",
Expand Down
10 changes: 5 additions & 5 deletions src/pages/tools/list/group/group.service.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { describe, expect, it } from 'vitest';

import { groupList, SplitOperatorType } from './service';
import { chunkList, SplitOperatorType } from './service';

describe('groupList', () => {
describe('chunkList', () => {
it('splits by symbol, groups, pads, and formats correctly', () => {
const input = 'a,b,c,d,e,f,g,h,i,j';
const splitOperatorType: SplitOperatorType = 'symbol';
Expand All @@ -18,7 +18,7 @@ describe('groupList', () => {

const expectedOutput = '[a-b-c] | [d-e-f] | [g-h-i] | [j-x-x]';

const result = groupList(
const result = chunkList(
splitOperatorType,
splitSeparator,
input,
Expand Down Expand Up @@ -49,7 +49,7 @@ describe('groupList', () => {

const expectedOutput = '(a,b,c,d) / (e,f,g,h) / (i,j)';

const result = groupList(
const result = chunkList(
splitOperatorType,
splitSeparator,
input,
Expand Down Expand Up @@ -80,7 +80,7 @@ describe('groupList', () => {

const expectedOutput = '<a:b> & <c:d> & <e:z>';

const result = groupList(
const result = chunkList(
splitOperatorType,
splitSeparator,
input,
Expand Down
145 changes: 112 additions & 33 deletions src/pages/tools/list/group/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ import { Box } from '@mui/material';
import React, { useState } from 'react';
import ToolTextInput from '@components/input/ToolTextInput';
import ToolTextResult from '@components/result/ToolTextResult';
import { groupList, SplitOperatorType } from './service';
import { chunkList, SplitOperatorType } from './service';
import SimpleRadio from '@components/options/SimpleRadio';
import TextFieldWithDesc from '@components/options/TextFieldWithDesc';
import { CardExampleType } from '@components/examples/ToolExamples';
import CheckboxWithDesc from '@components/options/CheckboxWithDesc';
import { formatNumber } from '../../../../utils/number';
import ToolContent from '@components/ToolContent';
Expand All @@ -23,24 +24,101 @@ const initialValues = {
padNonFullGroup: false,
paddingChar: '...'
};
const splitOperators: {
title: string;
description: string;
type: SplitOperatorType;
}[] = [

type InitialValuesType = typeof initialValues;

const splitOperators: SplitOperatorType[] = ['symbol', 'regex'];

const exampleCards: CardExampleType<InitialValuesType>[] = [
{
title: 'Use a Symbol for Splitting',
description: 'Delimit input list items with a character.',
type: 'symbol'
title: 'Group Hexagon Coordinates',
description:
'In this example, we group the coordinates of a regular hexagon. The input coordinates are given as a space-separated list "x1 y1 x2 y2 x3 y3 …". What we want to do is create vector point pairs such as "(x1, y1); (x2, y2); (x3, y3); …". To do that, we use the space character as the input coordinate separator, and to create vectors, we group them by pairs. We wrap the coordinates in parentheses, put a comma between the x and y group items, and a semicolon between individual groups.',
sampleText: '2.5 9.33 0 5 2.5 0.66 7.5 0.66 10 5 7.5 9.33',
sampleResult: `(2.5, 9.33); (0, 5); (2.5, 0.66); (7.5, 0.66); (10, 5); (7.5, 9.33)`,
sampleOptions: {
splitOperatorType: 'symbol',
splitSeparator: ' ',
groupNumber: 2,
itemSeparator: ', ',
leftWrap: '(',
rightWrap: ')',
groupSeparator: '; ',
deleteEmptyItems: true,
padNonFullGroup: false,
paddingChar: 'x'
}
},
{
title: 'Use a Regex for Splitting',
type: 'regex',
description: 'Delimit input list items with a regular expression.'
title: 'Chunks of Size 3',
description:
'This example demonstrates grouping of list items and creates 9 groups of 3 items. The input list contains all alphabet letters (26 letters, separated by a semicolon) and the output is groups of letter trigrams. As the last group is missing one letter, we enable padding and add the underscore symbol as the padding element.',
sampleText: 'a;b;c;d;e;f;g;h;i;j;k;l;m;n;o;p;q;r;s;t;u;v;w;x;y;z',
sampleResult: `[a, b, c]
[d, e, f]
[g, h, i]
[j, k, l]
[m, n, o]
[p, q, r]
[s, t, u]
[v, w, x]
[y, z, _]`,
sampleOptions: {
splitOperatorType: 'symbol',
splitSeparator: ';',
groupNumber: 3,
itemSeparator: ',',
leftWrap: '[',
rightWrap: ']',
groupSeparator: '\\n',
deleteEmptyItems: false,
padNonFullGroup: true,
paddingChar: '_'
}
},
{
title: 'Convert a List to a TSV',
description:
'In this example, we use our list item grouper to convert a food list to tab-separated values (TSV). As spaces are chaotically used between the items of the input list, we use the item separating regular expression "\\s+" to match them. We create a TSV with three columns (three groups), separate them with a tab character, and put newlines between the groups.',
sampleText: `beef buns
cake corn
crab
dill
fish
kiwi kale

lime meat
mint
milk
pear plum
pate
pork rice
soup
tuna
tart`,
sampleResult: `beef buns cake
corn crab dill
fish kiwi kale
lime meat mint
milk pear plum
pate pork rice
soup tuna tart`,
sampleOptions: {
splitOperatorType: 'regex',
splitSeparator: '\\s+',
groupNumber: 3,
itemSeparator: '\\t',
leftWrap: '',
rightWrap: '',
groupSeparator: '\\n',
deleteEmptyItems: true,
padNonFullGroup: false,
paddingChar: 'x'
}
}
];

export default function FindUnique({ title }: ToolComponentProps) {
export default function ChunkList({ title }: ToolComponentProps) {
const { t } = useTranslation('list');
const [input, setInput] = useState<string>('');
const [result, setResult] = useState<string>('');
Expand All @@ -59,7 +137,7 @@ export default function FindUnique({ title }: ToolComponentProps) {
} = optionsValues;

setResult(
groupList(
chunkList(
splitOperatorType,
splitSeparator,
input,
Expand All @@ -79,93 +157,94 @@ export default function FindUnique({ title }: ToolComponentProps) {
<ToolContent
title={title}
input={input}
exampleCards={exampleCards}
inputComponent={
<ToolTextInput
title={t('group.inputTitle')}
title={t('chunk.inputTitle')}
value={input}
onChange={setInput}
/>
}
resultComponent={
<ToolTextResult title={t('group.resultTitle')} value={result} />
<ToolTextResult title={t('chunk.resultTitle')} value={result} />
}
initialValues={initialValues}
getGroups={({ values, updateField }) => [
{
title: t('group.inputItemSeparator'),
title: t('chunk.inputItemSeparator'),
component: (
<Box>
{splitOperators.map(({ title, description, type }) => (
{splitOperators.map((type) => (
<SimpleRadio
key={type}
onClick={() => updateField('splitOperatorType', type)}
title={t(`group.splitOperators.${type}.title`)}
description={t(`group.splitOperators.${type}.description`)}
title={t(`chunk.splitOperators.${type}.title`)}
description={t(`chunk.splitOperators.${type}.description`)}
checked={values.splitOperatorType === type}
/>
))}
<TextFieldWithDesc
description={t('group.splitSeparatorDescription')}
description={t('chunk.splitSeparatorDescription')}
value={values.splitSeparator}
onOwnChange={(val) => updateField('splitSeparator', val)}
/>
</Box>
)
},
{
title: t('group.groupSizeAndSeparators'),
title: t('chunk.groupSizeAndSeparators'),
component: (
<Box>
<TextFieldWithDesc
value={values.groupNumber}
description={t('group.groupNumberDescription')}
description={t('chunk.groupNumberDescription')}
type={'number'}
onOwnChange={(value) =>
updateField('groupNumber', formatNumber(value, 1))
}
/>
<TextFieldWithDesc
value={values.itemSeparator}
description={t('group.itemSeparatorDescription')}
description={t('chunk.itemSeparatorDescription')}
onOwnChange={(value) => updateField('itemSeparator', value)}
/>
<TextFieldWithDesc
value={values.groupSeparator}
description={t('group.groupSeparatorDescription')}
description={t('chunk.groupSeparatorDescription')}
onOwnChange={(value) => updateField('groupSeparator', value)}
/>
<TextFieldWithDesc
value={values.leftWrap}
description={t('group.leftWrapDescription')}
description={t('chunk.leftWrapDescription')}
onOwnChange={(value) => updateField('leftWrap', value)}
/>
<TextFieldWithDesc
value={values.rightWrap}
description={t('group.rightWrapDescription')}
description={t('chunk.rightWrapDescription')}
onOwnChange={(value) => updateField('rightWrap', value)}
/>
</Box>
)
},
{
title: t('group.emptyItemsAndPadding'),
title: t('chunk.emptyItemsAndPadding'),
component: (
<Box>
<CheckboxWithDesc
title={t('group.deleteEmptyItems')}
description={t('group.deleteEmptyItemsDescription')}
title={t('chunk.deleteEmptyItems')}
description={t('chunk.deleteEmptyItemsDescription')}
checked={values.deleteEmptyItems}
onChange={(value) => updateField('deleteEmptyItems', value)}
/>
<CheckboxWithDesc
title={t('group.padNonFullGroups')}
description={t('group.padNonFullGroupsDescription')}
title={t('chunk.padNonFullGroups')}
description={t('chunk.padNonFullGroupsDescription')}
checked={values.padNonFullGroup}
onChange={(value) => updateField('padNonFullGroup', value)}
/>
<TextFieldWithDesc
value={values.paddingChar}
description={t('group.paddingCharDescription')}
description={t('chunk.paddingCharDescription')}
onOwnChange={(value) => updateField('paddingChar', value)}
/>
</Box>
Expand Down
12 changes: 6 additions & 6 deletions src/pages/tools/list/group/meta.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ import { defineTool } from '@tools/defineTool';
import { lazy } from 'react';

export const tool = defineTool('list', {
path: 'group',
icon: 'pajamas:group',
path: 'chunk',
icon: 'mdi:rhombus-split',

keywords: ['group'],
keywords: ['chuck', 'list', 'partition', 'split'],
component: lazy(() => import('./index')),
i18n: {
name: 'list:group.title',
description: 'list:group.description',
shortDescription: 'list:group.shortDescription',
name: 'list:chunk.title',
description: 'list:chunk.description',
shortDescription: 'list:chunk.shortDescription',
userTypes: ['generalUsers', 'developers']
}
});
Loading