Skip to content

Commit 7302f4a

Browse files
authored
Merge pull request #135
Text statistics
2 parents 91723e5 + 80df2eb commit 7302f4a

File tree

7 files changed

+492
-25
lines changed

7 files changed

+492
-25
lines changed

src/pages/tools/list/find-most-popular/service.ts

Lines changed: 19 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,9 @@
1+
import { itemCounter } from '@utils/string';
2+
13
export type SplitOperatorType = 'symbol' | 'regex';
24
export type DisplayFormat = 'count' | 'percentage' | 'total';
35
export type SortingMethod = 'count' | 'alphabetic';
46

5-
// Function that takes the array as arg and returns a dict of element occurrences and handle the ignoreItemCase
6-
function dictMaker(
7-
array: string[],
8-
ignoreItemCase: boolean
9-
): { [key: string]: number } {
10-
const dict: { [key: string]: number } = {};
11-
for (const item of array) {
12-
const key = ignoreItemCase ? item.toLowerCase() : item;
13-
dict[key] = (dict[key] || 0) + 1;
14-
}
15-
return dict;
16-
}
17-
187
// Function that sorts the dict created with dictMaker based on the chosen sorting method
198
function dictSorter(
209
dict: { [key: string]: number },
@@ -74,21 +63,27 @@ export function TopItemsList(
7463
sortingMethod: SortingMethod,
7564
displayFormat: DisplayFormat,
7665
splitSeparator: string,
77-
input: string,
66+
input: string | string[],
7867
deleteEmptyItems: boolean,
7968
ignoreItemCase: boolean,
8069
trimItems: boolean
8170
): string {
71+
if (!input) return '';
72+
8273
let array: string[];
83-
switch (splitOperatorType) {
84-
case 'symbol':
85-
array = input.split(splitSeparator);
86-
break;
87-
case 'regex':
88-
array = input
89-
.split(new RegExp(splitSeparator))
90-
.filter((item) => item !== '');
91-
break;
74+
if (typeof input === 'string') {
75+
switch (splitOperatorType) {
76+
case 'symbol':
77+
array = input.split(splitSeparator);
78+
break;
79+
case 'regex':
80+
array = input
81+
.split(new RegExp(splitSeparator))
82+
.filter((item) => item !== '');
83+
break;
84+
}
85+
} else {
86+
array = input;
9287
}
9388

9489
// Trim items if required
@@ -102,7 +97,7 @@ export function TopItemsList(
10297
}
10398

10499
// Transform the array into dict
105-
const unsortedDict = dictMaker(array, ignoreItemCase);
100+
const unsortedDict = itemCounter(array, ignoreItemCase);
106101

107102
// Sort the list if required
108103
const sortedDict = dictSorter(unsortedDict, sortingMethod);

src/pages/tools/string/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { tool as stringReplace } from './text-replacer/meta';
1515
import { tool as stringRepeat } from './repeat/meta';
1616
import { tool as stringTruncate } from './truncate/meta';
1717
import { tool as stringBase64 } from './base64/meta';
18+
import { tool as stringStatistic } from './statistic/meta';
1819

1920
export const stringTools = [
2021
stringSplit,
@@ -33,5 +34,6 @@ export const stringTools = [
3334
stringQuote,
3435
stringRotate,
3536
stringRot13,
36-
stringBase64
37+
stringBase64,
38+
stringStatistic
3739
];
Lines changed: 298 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,298 @@
1+
import { Box } from '@mui/material';
2+
import { useState } from 'react';
3+
import ToolTextResult from '@components/result/ToolTextResult';
4+
import { GetGroupsType } from '@components/options/ToolOptions';
5+
import { textStatistics } from './service';
6+
import ToolTextInput from '@components/input/ToolTextInput';
7+
import { InitialValuesType } from './types';
8+
import ToolContent from '@components/ToolContent';
9+
import { CardExampleType } from '@components/examples/ToolExamples';
10+
import { ToolComponentProps } from '@tools/defineTool';
11+
import TextFieldWithDesc from '@components/options/TextFieldWithDesc';
12+
import CheckboxWithDesc from '@components/options/CheckboxWithDesc';
13+
14+
const initialValues: InitialValuesType = {
15+
emptyLines: false,
16+
sentenceDelimiters: '',
17+
wordDelimiters: '',
18+
characterCount: false,
19+
wordCount: false
20+
};
21+
22+
const exampleCards: CardExampleType<InitialValuesType>[] = [
23+
{
24+
title: 'Text Statistics without any Flag',
25+
description:
26+
'This example shows basic text statistics without any additional flags.',
27+
sampleText:
28+
'Giraffes have long necks that can be up to 6 feet (1.8 meters) long, but they only have 7 neck vertebrae, the same as humans.',
29+
sampleResult: `Text Statistics
30+
==================
31+
Characters: 125
32+
Words: 26
33+
Lines: 1
34+
Sentences: 1
35+
Paragraphs: 1`,
36+
sampleOptions: initialValues
37+
},
38+
{
39+
title: 'Text Statistics with Characters Frequency',
40+
description:
41+
'This example shows basic text statistics with characters frequency.',
42+
sampleText: `The Great Barrier Reef is the world's largest coral reef system, located off the coast of Australia. It consists of over 2,900 individual reefs and 900 islands. The reef is home to thousands of species of marine life, including fish, sea turtles, sharks, and dolphins. It is also a popular tourist destination, attracting millions of visitors every year. However, the reef is facing many threats, including climate change, pollution, and overfishing. Conservation efforts are being made to protect this unique and valuable ecosystem for future generations.`,
43+
sampleResult: `Text Statistics
44+
==================
45+
Characters: 556
46+
Words: 87
47+
Lines: 1
48+
Sentences: 1
49+
Paragraphs: 1
50+
51+
Characters Frequency
52+
==================
53+
0: 4 (0.72%)
54+
2: 1 (0.18%)
55+
9: 2 (0.36%)
56+
␣: 85 (15.29%)
57+
e: 51 (9.17%)
58+
i: 40 (7.19%)
59+
s: 40 (7.19%)
60+
t: 39 (7.01%)
61+
a: 37 (6.65%)
62+
o: 34 (6.12%)
63+
r: 33 (5.94%)
64+
n: 29 (5.22%)
65+
l: 21 (3.78%)
66+
f: 20 (3.60%)
67+
h: 15 (2.70%)
68+
d: 15 (2.70%)
69+
c: 14 (2.52%)
70+
u: 14 (2.52%)
71+
,: 11 (1.98%)
72+
g: 10 (1.80%)
73+
m: 8 (1.44%)
74+
v: 8 (1.44%)
75+
.: 6 (1.08%)
76+
p: 6 (1.08%)
77+
y: 5 (0.90%)
78+
b: 3 (0.54%)
79+
w: 2 (0.36%)
80+
': 1 (0.18%)
81+
k: 1 (0.18%)
82+
q: 1 (0.18%)`,
83+
sampleOptions: {
84+
emptyLines: false,
85+
sentenceDelimiters: '',
86+
wordDelimiters: '',
87+
characterCount: true,
88+
wordCount: false
89+
}
90+
},
91+
{
92+
title: 'Text Statistics with Characters and Words Frequencies',
93+
description:
94+
'This example shows basic text statistics with characters and words frequencies.',
95+
sampleText: `The Great Barrier Reef is the world's largest coral reef system, located off the coast of Australia. It consists of over 2,900 individual reefs and 900 islands. The reef is home to thousands of species of marine life, including fish, sea turtles, sharks, and dolphins. It is also a popular tourist destination, attracting millions of visitors every year. However, the reef is facing many threats, including climate change, pollution, and overfishing. Conservation efforts are being made to protect this unique and valuable ecosystem for future generations.`,
96+
sampleResult: `Text Statistics
97+
==================
98+
Characters: 556
99+
Words: 87
100+
Lines: 1
101+
Sentences: 1
102+
Paragraphs: 1
103+
104+
Words Frequency
105+
==================
106+
2: 1 (1.15%)
107+
900: 2 (2.30%)
108+
the: 5 (5.75%)
109+
of: 5 (5.75%)
110+
reef: 4 (4.60%)
111+
is: 4 (4.60%)
112+
and: 4 (4.60%)
113+
it: 2 (2.30%)
114+
to: 2 (2.30%)
115+
including: 2 (2.30%)
116+
great: 1 (1.15%)
117+
barrier: 1 (1.15%)
118+
world's: 1 (1.15%)
119+
largest: 1 (1.15%)
120+
coral: 1 (1.15%)
121+
system: 1 (1.15%)
122+
located: 1 (1.15%)
123+
off: 1 (1.15%)
124+
coast: 1 (1.15%)
125+
australia: 1 (1.15%)
126+
consists: 1 (1.15%)
127+
over: 1 (1.15%)
128+
individual: 1 (1.15%)
129+
reefs: 1 (1.15%)
130+
islands: 1 (1.15%)
131+
home: 1 (1.15%)
132+
thousands: 1 (1.15%)
133+
species: 1 (1.15%)
134+
marine: 1 (1.15%)
135+
life: 1 (1.15%)
136+
fish: 1 (1.15%)
137+
sea: 1 (1.15%)
138+
turtles: 1 (1.15%)
139+
sharks: 1 (1.15%)
140+
dolphins: 1 (1.15%)
141+
also: 1 (1.15%)
142+
a: 1 (1.15%)
143+
popular: 1 (1.15%)
144+
tourist: 1 (1.15%)
145+
destination: 1 (1.15%)
146+
attracting: 1 (1.15%)
147+
millions: 1 (1.15%)
148+
visitors: 1 (1.15%)
149+
every: 1 (1.15%)
150+
year: 1 (1.15%)
151+
however: 1 (1.15%)
152+
facing: 1 (1.15%)
153+
many: 1 (1.15%)
154+
threats: 1 (1.15%)
155+
climate: 1 (1.15%)
156+
change: 1 (1.15%)
157+
pollution: 1 (1.15%)
158+
overfishing: 1 (1.15%)
159+
conservation: 1 (1.15%)
160+
efforts: 1 (1.15%)
161+
are: 1 (1.15%)
162+
being: 1 (1.15%)
163+
made: 1 (1.15%)
164+
protect: 1 (1.15%)
165+
this: 1 (1.15%)
166+
unique: 1 (1.15%)
167+
valuable: 1 (1.15%)
168+
ecosystem: 1 (1.15%)
169+
for: 1 (1.15%)
170+
future: 1 (1.15%)
171+
generations: 1 (1.15%)
172+
173+
Characters Frequency
174+
==================
175+
0: 4 (0.72%)
176+
2: 1 (0.18%)
177+
9: 2 (0.36%)
178+
␣: 85 (15.29%)
179+
e: 51 (9.17%)
180+
i: 40 (7.19%)
181+
s: 40 (7.19%)
182+
t: 39 (7.01%)
183+
a: 37 (6.65%)
184+
o: 34 (6.12%)
185+
r: 33 (5.94%)
186+
n: 29 (5.22%)
187+
l: 21 (3.78%)
188+
f: 20 (3.60%)
189+
h: 15 (2.70%)
190+
d: 15 (2.70%)
191+
c: 14 (2.52%)
192+
u: 14 (2.52%)
193+
,: 11 (1.98%)
194+
g: 10 (1.80%)
195+
m: 8 (1.44%)
196+
v: 8 (1.44%)
197+
.: 6 (1.08%)
198+
p: 6 (1.08%)
199+
y: 5 (0.90%)
200+
b: 3 (0.54%)
201+
w: 2 (0.36%)
202+
': 1 (0.18%)
203+
k: 1 (0.18%)
204+
q: 1 (0.18%)`,
205+
sampleOptions: {
206+
emptyLines: false,
207+
sentenceDelimiters: '',
208+
wordDelimiters: '',
209+
characterCount: true,
210+
wordCount: true
211+
}
212+
}
213+
];
214+
215+
export default function Truncate({
216+
title,
217+
longDescription
218+
}: ToolComponentProps) {
219+
const [input, setInput] = useState<string>('');
220+
const [result, setResult] = useState<string>('');
221+
222+
function compute(initialValues: InitialValuesType, input: string) {
223+
setResult(textStatistics(input, initialValues));
224+
}
225+
226+
const getGroups: GetGroupsType<InitialValuesType> = ({
227+
values,
228+
updateField
229+
}) => [
230+
{
231+
title: 'Delimiters Options',
232+
component: (
233+
<Box>
234+
<TextFieldWithDesc
235+
value={values.sentenceDelimiters}
236+
onOwnChange={(val) => updateField('sentenceDelimiters', val)}
237+
placeholder="e.g. ., !, ?, ..."
238+
description={
239+
'Enter custom characters used to delimit sentences in your language (separated by comma) or leave it blank for default.'
240+
}
241+
/>
242+
<TextFieldWithDesc
243+
value={values.wordDelimiters}
244+
onOwnChange={(val) => updateField('wordDelimiters', val)}
245+
placeholder="eg. \\s.,;:!?\”«»()…"
246+
description={
247+
'Enter custom Regex to count Words or leave it blank for default.'
248+
}
249+
/>
250+
</Box>
251+
)
252+
},
253+
{
254+
title: 'Statistics Options',
255+
component: (
256+
<Box>
257+
<CheckboxWithDesc
258+
checked={values.wordCount}
259+
onChange={(value) => updateField('wordCount', value)}
260+
title="Word Frequency Analysis"
261+
description="Count how often each word appears in the text"
262+
/>
263+
<CheckboxWithDesc
264+
checked={values.characterCount}
265+
onChange={(value) => updateField('characterCount', value)}
266+
title="Character Frequency Analysis"
267+
description="Count how often each character appears in the text"
268+
/>
269+
<CheckboxWithDesc
270+
checked={values.emptyLines}
271+
onChange={(value) => updateField('emptyLines', value)}
272+
title="Include Empty Lines"
273+
description="Include blank lines when counting lines"
274+
/>
275+
</Box>
276+
)
277+
}
278+
];
279+
280+
return (
281+
<ToolContent
282+
title={title}
283+
initialValues={initialValues}
284+
getGroups={getGroups}
285+
compute={compute}
286+
input={input}
287+
setInput={setInput}
288+
inputComponent={
289+
<ToolTextInput title={'Input text'} value={input} onChange={setInput} />
290+
}
291+
resultComponent={
292+
<ToolTextResult title={'Text Statistics'} value={result} />
293+
}
294+
toolInfo={{ title: `What is a ${title}?`, description: longDescription }}
295+
exampleCards={exampleCards}
296+
/>
297+
);
298+
}

0 commit comments

Comments
 (0)