Skip to content

Commit

Permalink
feat: add an option to weight elements by the # of siblings instead o…
Browse files Browse the repository at this point in the history
…f # of subnodes

If two JSONs has only two keys, and one of them is in both JSON a single number, let's say `3`, but in the other key, they both have a four-level nested json with thousands of arrays, sub-objects, sub-arrays and text and numbers, for which they are only completely different, would you say they are 50% equal (1 out of 2 keys) or almost completely different?

Options are:
* All the nodes weight the same, so a root node with several nested nodes would weight more than the other siblings.
* Each node at a given level have a weight in the total equal to 1 / the number of sibblings. And its sub-tree nodes add to that fraction only.
  • Loading branch information
diegopamio committed Nov 4, 2020
1 parent e656f02 commit 4792dd8
Show file tree
Hide file tree
Showing 13 changed files with 277 additions and 82 deletions.
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,10 @@ If two JSONs has only two keys, and one of them is in both JSON a single number,

Options are:
* All the nodes weight the same, so a root node with several nested nodes would weight more than the other siblings.
* Each node at a given level have a weight in the total equal to 1 / the number of sibblings. And its sub-tree nodes add to that fraction only.

_(Pictue and option setting for this one comming in future releases)_
![Option 1](public/images/elementWeight-descendantsCount.png)

* Each node at a given level have a weight in the total equal to 1 / the number of siblings. And its sub-tree nodes add to that fraction only.
![Option 2](public/images/elementWeight-siblingsProportion.png)
## How to count missing branches

If some element is missing in one of the JSON but present in the other, you could choose to count that as:
Expand All @@ -78,7 +78,7 @@ If some element is missing in one of the JSON but present in the other, you coul
* number of root nodes in one side, and 0 in the other.
* number of all the nested nodes in one side, and 0 in the other.

_(Pictue and option setting for this one comming in future releases)_
_(Picture and option setting for this one comming in future releases)_

# What it DOESN'T has
- i18n
Expand All @@ -94,7 +94,7 @@ _(Pictue and option setting for this one comming in future releases)_
* [x] ~~FullStory (to analyze user’s behavior).~~
* [x] ~~Crisp (as the only support I’ll offer to end users).~~
* Algorithm options:
* [ ] Weighed by level vs weighed by sub-tree size scoring
* [x] ~~Weighed by level vs weighed by sub-tree size scoring.~~
* [x] ~~Order-Forced vs Any-to-any array subtree comparison~~
* [ ] Counting 0/1 or 0/<subtree size> when some ket is present in only one of the sides
* UI/Features:
Expand Down
41 changes: 14 additions & 27 deletions components/OptionsPanel/ArrayCompareOptionPanel.jsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
import Box from '@material-ui/core/Box';
import React from 'react';
import { CardContent, CardHeader, Grid } from '@material-ui/core';
import Card from '@material-ui/core/Card';
import { TabPanel } from '~/components/OptionsPanel/TabPanel';
import { OptionsAlternatives } from '~/components/OptionsPanel/OptionsAlternatives';
import PropTypes from 'prop-types';
import { OptionsTabPanel } from '~/components/OptionsPanel/OptionsTabPanel';
import { SCORE_SETTINGS_VALUES, SCORING_SETTINGS_KEYS } from '~/utils/score/constants';

const { ARRAY_POSITION_MATCH } = SCORING_SETTINGS_KEYS;
Expand All @@ -19,27 +16,17 @@ const explanation = 'This setting determines whether arrays inside the JSON file
+ ' nature and thus, they may be considered EQUAL when their equal elements are positioned at different'
+ ' indexes.';

export const ArrayCompareOptionPanel = ({ value, index, dir }) => (
<TabPanel value={value} index={index} dir={dir}>
<Box p={3}>
<Grid container spacing={2}>
<Grid item xs={12} sm={4}>
<Card>
<CardHeader title="Explanation" />
<CardContent>{explanation}</CardContent>
</Card>
</Grid>
<Grid item xs={12} sm={8}>
<Card>
<CardHeader title="Select Option" />
<CardContent>
<OptionsAlternatives optionKey={ARRAY_POSITION_MATCH} alternatives={alternatives} />
</CardContent>
</Card>
</Grid>
</Grid>
</Box>
</TabPanel>
export const ArrayCompareOptionPanel = ({ value, index }) => (
<OptionsTabPanel
value={value}
index={index}
explanation={explanation}
alternatives={alternatives}
optionKey={ARRAY_POSITION_MATCH}
/>
);

ArrayCompareOptionPanel.propTypes = TabPanel.propTypes;
ArrayCompareOptionPanel.propTypes = {
index: PropTypes.number.isRequired,
value: PropTypes.number.isRequired,
};
33 changes: 33 additions & 0 deletions components/OptionsPanel/ElementWeightOptionPanel.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import React from 'react';
import PropTypes from 'prop-types';
import { OptionsTabPanel } from '~/components/OptionsPanel/OptionsTabPanel';
import { SCORE_SETTINGS_VALUES, SCORING_SETTINGS_KEYS } from '~/utils/score/constants';

const { ELEMENT_WEIGHT } = SCORING_SETTINGS_KEYS;
const { SIBLINGS_PROPORTION, DESCENDANTS_COUNT } = SCORE_SETTINGS_VALUES[ELEMENT_WEIGHT];

const alternatives = [
{ label: 'Descendants Count', value: DESCENDANTS_COUNT },
{ label: 'Siblings Proportion', value: SIBLINGS_PROPORTION },
];

const explanation = 'If two JSONs has only two keys, and one of them is in both JSON a single number, let\'s say `3`,'
+ ' but in the other key, they both have a four-level nested json with thousands of arrays, sub-objects, sub-arrays'
+ ' and text and numbers, for which they are only completely different, would you say they are 50% equal'
+ ' (1 out of 2 keys) or almost completely different? This setting allows you to control exactly that.';

// const explanation = 'This setting '
export const ElementWeightOptionPanel = ({ value, index }) => (
<OptionsTabPanel
value={value}
index={index}
explanation={explanation}
alternatives={alternatives}
optionKey={ELEMENT_WEIGHT}
/>
);

ElementWeightOptionPanel.propTypes = {
index: PropTypes.number.isRequired,
value: PropTypes.number.isRequired,
};
11 changes: 3 additions & 8 deletions components/OptionsPanel/OptionsPanel.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { Grid, useTheme } from '@material-ui/core';
import * as PropTypes from 'prop-types';
import { CollapsiblePanel } from '~/components/CollapsiblePanel';
import { ArrayCompareOptionPanel } from '~/components/OptionsPanel/ArrayCompareOptionPanel';
import { TabPanel } from '~/components/OptionsPanel/TabPanel';
import { ElementWeightOptionPanel } from '~/components/OptionsPanel/ElementWeightOptionPanel';

const useStyles = makeStyles({
root: {
Expand Down Expand Up @@ -51,13 +51,8 @@ const TabPanels = ({ index, onChangeIndex }) => {
index={index}
onChangeIndex={onChangeIndex}
>
<ArrayCompareOptionPanel value={index} index={0} dir={theme.direction} />
<TabPanel value={index} index={1} dir={theme.direction}>
Coming soon!
</TabPanel>
<TabPanel value={index} index={2} dir={theme.direction}>
Coming soon!
</TabPanel>
<ArrayCompareOptionPanel value={index} index={0} />
<ElementWeightOptionPanel value={index} index={1} />
</SwipeableViews>
);
};
Expand Down
61 changes: 61 additions & 0 deletions components/OptionsPanel/OptionsTabPanel.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/* eslint-disable react/jsx-props-no-spreading */
import PropTypes from 'prop-types';
import React from 'react';
import Box from '@material-ui/core/Box';
import {
CardContent, CardHeader, Grid, useTheme,
} from '@material-ui/core';
import Card from '@material-ui/core/Card';
import { OptionsAlternatives } from '~/components/OptionsPanel/OptionsAlternatives';
import { SCORING_SETTINGS_KEYS } from '~/utils/score/constants';

export const OptionsTabPanel = ({
children, value, index, explanation, alternatives, optionKey, ...other
}) => {
const theme = useTheme();
return (
<div
role="tabpanel"
hidden={value !== index}
id={`tabpanel-${index}`}
aria-labelledby={`tab-${index}`}
dir={theme.direction}
{...other}
>
{value === index && (
<Box p={3}>
<Box p={3}>
<Grid container spacing={2}>
<Grid item xs={12} sm={4}>
<Card>
<CardHeader title="Explanation" />
<CardContent>{explanation}</CardContent>
</Card>
</Grid>
<Grid item xs={12} sm={8}>
<Card>
<CardHeader title="Select Option" />
<CardContent>
<OptionsAlternatives optionKey={optionKey} alternatives={alternatives} />
</CardContent>
</Card>
</Grid>
</Grid>
</Box>
</Box>
)}
</div>
);
};

OptionsTabPanel.propTypes = {
children: PropTypes.node.isRequired,
index: PropTypes.number.isRequired,
value: PropTypes.number.isRequired,
explanation: PropTypes.string.isRequired,
optionKey: PropTypes.oneOf(Object.values(SCORING_SETTINGS_KEYS)).isRequired,
alternatives: PropTypes.arrayOf(PropTypes.shape({
value: PropTypes.string.isRequired,
label: PropTypes.string.isRequired,
})).isRequired,
};
28 changes: 0 additions & 28 deletions components/OptionsPanel/TabPanel.jsx

This file was deleted.

46 changes: 46 additions & 0 deletions cypress/integration/options/element-weight.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
const OptionalHeaderTitle = 'Options';
// ToDo: Add simpler yet more evident tests for the difference in the algorithm.
describe('the Array Order option', () => {
beforeEach(() => {
cy.visit('/');
cy.contains('.MuiCardHeader-root', OptionalHeaderTitle).find('button').click();
cy.contains('Depth Weight').click();
});
it('should have an explanation section', () => {
cy.contains('Explanation');
});
it('should have a "Select Option" section', () => {
cy.contains('Select Option');
});
it('should have two options', () => {
cy.contains('.MuiCard-root', OptionalHeaderTitle).find('input[type="radio"]').should('have.length', 2);
});
describe('when selecting descendant count', () => {
it('should return a higher score', () => {
// arrange
const fileNameA = 'BreweriesMaster.json';
const fileNameB = 'BreweriesSample4.json';
cy.drop('File A', fileNameA);
cy.drop('File B', fileNameB);
// act
cy.contains('Descendants Count').click();
cy.contains('Compare').click();
// assert
cy.contains('35.7%');
});
});
describe('when selecting siblings proportion', () => {
it('should return a lower score', () => {
// arrange
const fileNameA = 'BreweriesMaster.json';
const fileNameB = 'BreweriesSample4.json';
cy.drop('File A', fileNameA);
cy.drop('File B', fileNameB);
// act
cy.contains('Siblings Proportion').click();
cy.contains('Compare').click();
// assert
cy.contains('34.2%');
});
});
});
1 change: 0 additions & 1 deletion cypress/integration/options/panel.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ describe('the Options panel', () => {
// assert
cy.contains('Array Order');
cy.contains('Depth Weight');
cy.contains('Missing Branch');
});
it('should have the first option as selected', () => {
cy.contains('.MuiCardHeader-root', OptionalHeaderTitle).find('button').click();
Expand Down
Binary file added public/images/elementWeight-descendantsCount.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 4792dd8

Please sign in to comment.