mirror of
https://github.com/iib0011/omni-tools.git
synced 2025-09-20 06:29:32 +02:00
feat: swap-csv-columns fixed
This commit is contained in:
@@ -11,8 +11,10 @@ import CheckboxWithDesc from '@components/options/CheckboxWithDesc';
|
||||
import SimpleRadio from '@components/options/SimpleRadio';
|
||||
import TextFieldWithDesc from '@components/options/TextFieldWithDesc';
|
||||
import { csvColumnsSwap } from './service';
|
||||
import { getCsvHeaders } from '@utils/csv';
|
||||
import { InitialValuesType } from './types';
|
||||
|
||||
const initialValues = {
|
||||
const initialValues: InitialValuesType = {
|
||||
fromPositionStatus: true,
|
||||
toPositionStatus: true,
|
||||
fromPosition: '1',
|
||||
@@ -25,7 +27,7 @@ const initialValues = {
|
||||
commentCharacter: '#',
|
||||
emptyLines: true
|
||||
};
|
||||
type InitialValuesType = typeof initialValues;
|
||||
|
||||
const exampleCards: CardExampleType<InitialValuesType>[] = [
|
||||
{
|
||||
title: 'Move the Key Column to the First Position',
|
||||
@@ -48,8 +50,8 @@ Utah,Zion Park`,
|
||||
toPositionStatus: true,
|
||||
fromPosition: '1',
|
||||
toPosition: '2',
|
||||
fromHeader: '',
|
||||
toHeader: '',
|
||||
fromHeader: 'park_name',
|
||||
toHeader: 'location',
|
||||
emptyValuesFilling: false,
|
||||
customFiller: '*',
|
||||
deleteComment: false,
|
||||
@@ -120,7 +122,7 @@ Xiaomi,13 Ultra,Android,6.6″,$849`,
|
||||
fromPosition: '1',
|
||||
toPosition: '4',
|
||||
fromHeader: 'ScreenSize',
|
||||
toHeader: 'Function',
|
||||
toHeader: 'OS',
|
||||
emptyValuesFilling: true,
|
||||
customFiller: 'x',
|
||||
deleteComment: true,
|
||||
@@ -137,25 +139,16 @@ export default function CsvToTsv({
|
||||
const [input, setInput] = useState<string>('');
|
||||
const [result, setResult] = useState<string>('');
|
||||
|
||||
const compute = (optionsValues: typeof initialValues, input: string) => {
|
||||
setResult(
|
||||
csvColumnsSwap(
|
||||
input,
|
||||
optionsValues.fromPositionStatus,
|
||||
optionsValues.fromPosition,
|
||||
optionsValues.toPositionStatus,
|
||||
optionsValues.toPosition,
|
||||
optionsValues.fromHeader,
|
||||
optionsValues.toHeader,
|
||||
optionsValues.emptyValuesFilling,
|
||||
optionsValues.customFiller,
|
||||
optionsValues.deleteComment,
|
||||
optionsValues.commentCharacter,
|
||||
optionsValues.emptyLines
|
||||
)
|
||||
);
|
||||
const compute = (optionsValues: InitialValuesType, input: string) => {
|
||||
setResult(csvColumnsSwap(input, optionsValues));
|
||||
};
|
||||
|
||||
const headers = getCsvHeaders(input);
|
||||
const headerOptions = headers.map((item) => ({
|
||||
label: `${item}`,
|
||||
value: item
|
||||
}));
|
||||
|
||||
const getGroups: GetGroupsType<InitialValuesType> = ({
|
||||
values,
|
||||
updateField
|
||||
@@ -175,18 +168,21 @@ export default function CsvToTsv({
|
||||
value={values.fromPosition}
|
||||
onOwnChange={(val) => updateField('fromPosition', val)}
|
||||
type="number"
|
||||
inputProps={{ min: 1, max: headers.length }}
|
||||
/>
|
||||
)}
|
||||
|
||||
<SimpleRadio
|
||||
onClick={() => updateField('fromPositionStatus', false)}
|
||||
title="Set Column-From Header"
|
||||
checked={!values.fromPositionStatus}
|
||||
/>
|
||||
{!values.fromPositionStatus && (
|
||||
<TextFieldWithDesc
|
||||
description={'Header of the first column you want to swap'}
|
||||
value={values.fromHeader}
|
||||
onOwnChange={(val) => updateField('fromHeader', val)}
|
||||
<SelectWithDesc
|
||||
selected={values.fromHeader}
|
||||
options={headerOptions}
|
||||
onChange={(value) => updateField('fromHeader', value)}
|
||||
description={'Header of the first column you want to swap.'}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
@@ -207,6 +203,7 @@ export default function CsvToTsv({
|
||||
value={values.toPosition}
|
||||
onOwnChange={(val) => updateField('toPosition', val)}
|
||||
type="number"
|
||||
inputProps={{ min: 1, max: headers.length }}
|
||||
/>
|
||||
)}
|
||||
<SimpleRadio
|
||||
@@ -215,10 +212,11 @@ export default function CsvToTsv({
|
||||
checked={!values.toPositionStatus}
|
||||
/>
|
||||
{!values.toPositionStatus && (
|
||||
<TextFieldWithDesc
|
||||
description={'Header of the second column you want to swap'}
|
||||
value={values.toHeader}
|
||||
onOwnChange={(val) => updateField('toHeader', val)}
|
||||
<SelectWithDesc
|
||||
selected={values.toHeader}
|
||||
options={headerOptions}
|
||||
onChange={(value) => updateField('toHeader', value)}
|
||||
description={'Header of the second column you want to swap..'}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
@@ -261,7 +259,7 @@ export default function CsvToTsv({
|
||||
title="Delete Comments"
|
||||
description="if checked, comments given by the following character will be deleted"
|
||||
/>
|
||||
{!values.emptyValuesFilling && (
|
||||
{values.deleteComment && (
|
||||
<TextFieldWithDesc
|
||||
description={
|
||||
'Specify the character used to start comments in the input CSV (and if needed remove them via checkbox above)'
|
||||
|
@@ -1,21 +1,17 @@
|
||||
import { splitCsv } from '@utils/csv';
|
||||
import { InitialValuesType } from './types';
|
||||
|
||||
function retrieveFromAndTo(
|
||||
headerRow: string[],
|
||||
fromPositionStatus: boolean,
|
||||
toPositionStatus: boolean,
|
||||
fromPosition: string | '',
|
||||
toPosition: string | '',
|
||||
fromHeader: string | '',
|
||||
toHeader: string | ''
|
||||
options: InitialValuesType
|
||||
): number[] {
|
||||
const from = fromPositionStatus
|
||||
? Number(fromPosition)
|
||||
: headerRow.findIndex((header) => header === fromHeader) + 1;
|
||||
const from = options.fromPositionStatus
|
||||
? Number(options.fromPosition)
|
||||
: headerRow.findIndex((header) => header === options.fromHeader) + 1;
|
||||
|
||||
const to = toPositionStatus
|
||||
? Number(toPosition)
|
||||
: headerRow.findIndex((header) => header === toHeader) + 1;
|
||||
const to = options.toPositionStatus
|
||||
? Number(options.toPosition)
|
||||
: headerRow.findIndex((header) => header === options.toHeader) + 1;
|
||||
|
||||
if (from <= 0 || to <= 0)
|
||||
throw new Error('Invalid column positions. Check headers or positions.');
|
||||
@@ -37,44 +33,28 @@ function swap(lines: string[][], from: number, to: number): string[][] {
|
||||
});
|
||||
}
|
||||
|
||||
export function csvColumnsSwap(
|
||||
input: string,
|
||||
fromPositionStatus: boolean,
|
||||
fromPosition: string | '',
|
||||
toPositionStatus: boolean,
|
||||
toPosition: string | '',
|
||||
fromHeader: string | '',
|
||||
toHeader: string | '',
|
||||
emptyValuesFilling: boolean,
|
||||
customFiller: string | '',
|
||||
deleteComment: boolean,
|
||||
commentCharacter: string | '',
|
||||
emptyLines: boolean
|
||||
) {
|
||||
export function csvColumnsSwap(input: string, options: InitialValuesType) {
|
||||
if (!input) {
|
||||
return '';
|
||||
}
|
||||
// split csv input and remove comments
|
||||
const rows = splitCsv(input, deleteComment, commentCharacter, emptyLines);
|
||||
const rows = splitCsv(
|
||||
input,
|
||||
options.deleteComment,
|
||||
options.commentCharacter,
|
||||
options.emptyLines
|
||||
);
|
||||
|
||||
const columnCount = Math.max(...rows.map((row) => row.length));
|
||||
for (let i = 0; i < rows.length; i++) {
|
||||
for (let j = 0; j < columnCount; j++) {
|
||||
if (!rows[i][j]) {
|
||||
rows[i][j] = emptyValuesFilling ? '' : customFiller;
|
||||
rows[i][j] = options.emptyValuesFilling ? '' : options.customFiller;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const positions = retrieveFromAndTo(
|
||||
rows[0],
|
||||
fromPositionStatus,
|
||||
toPositionStatus,
|
||||
fromPosition,
|
||||
toPosition,
|
||||
fromHeader,
|
||||
toHeader
|
||||
);
|
||||
const positions = retrieveFromAndTo(rows[0], options);
|
||||
|
||||
const result = swap(rows, positions[0], positions[1]);
|
||||
return result.join('\n');
|
||||
|
13
src/pages/tools/csv/swap-csv-columns/types.ts
Normal file
13
src/pages/tools/csv/swap-csv-columns/types.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
export type InitialValuesType = {
|
||||
fromPositionStatus: boolean;
|
||||
fromPosition: string | '';
|
||||
toPositionStatus: boolean;
|
||||
toPosition: string | '';
|
||||
fromHeader: string | '';
|
||||
toHeader: string | '';
|
||||
emptyValuesFilling: boolean;
|
||||
customFiller: string | '';
|
||||
deleteComment: boolean;
|
||||
commentCharacter: string | '';
|
||||
emptyLines: boolean;
|
||||
};
|
@@ -13,7 +13,7 @@ export function splitCsv(
|
||||
let rows = input.split('\n').map((row) => row.split(','));
|
||||
|
||||
// Remove comments if deleteComment is true
|
||||
if (deleteComment) {
|
||||
if (deleteComment && commentCharacter) {
|
||||
rows = rows.filter((row) => !row[0].trim().startsWith(commentCharacter));
|
||||
}
|
||||
|
||||
@@ -24,3 +24,13 @@ export function splitCsv(
|
||||
|
||||
return rows;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the headers from a CSV string .
|
||||
* @param {string} input - The CSV input string.
|
||||
* @returns {string[]} - The CSV header as a 1D array.
|
||||
*/
|
||||
export function getCsvHeaders(csvString: string): string[] {
|
||||
const rows = csvString.split('\n').map((row) => row.split(','));
|
||||
return rows.length > 0 ? rows[0].map((header) => header.trim()) : [];
|
||||
}
|
||||
|
Reference in New Issue
Block a user