feat: swap-csv-columns fixed

This commit is contained in:
Chesterkxng
2025-04-01 15:40:19 +00:00
parent d56af77ed5
commit 477117cd8c
4 changed files with 70 additions and 69 deletions

View File

@@ -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)'

View File

@@ -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');

View 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;
};

View File

@@ -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()) : [];
}