feat: solution for 'Повторный экзамен #2: Сравнительный обзор 3 сущностей (Tavily)'

This commit is contained in:
2026-06-29 16:55:55 +03:00
parent c7e2183e3e
commit 29f9717e89
5 changed files with 155 additions and 141 deletions
+35 -42
View File
@@ -1,61 +1,54 @@
# Tavily Compare # Compare Three Entities
This Node.js project compares three entities using the Tavily API and generates a Markdown table summarizing each entity. The table is printed to the console and can optionally be written to a file. A small utility library that compares three JavaScript objects and reports the differences between them.
The comparison is deep, meaning nested objects are compared recursively. The result is an array of difference objects, each containing:
## Prerequisites - `key`: The dotseparated path to the differing property.
- `values`: An array of the values from the three objects in the order `[a, b, c]`.
- Node.js v18 or newer (for native ES modules support) ## Installation
- A Tavily API key
## Setup ```bash
npm install compare-three-entities
1. **Clone the repository** (or copy the files into a new directory): ```
```bash
git clone https://github.com/your-username/tavily-compare.git
cd tavily-compare
```
2. **Install dependencies**:
```bash
npm install
```
3. **Create a `.env` file** in the project root and add your Tavily API key:
```env
TAVILY_API_KEY=your_api_key_here
```
## Usage ## Usage
Run the script with three entity names: ```js
const compare = require('compare-three-entities');
```bash const a = { name: 'Alice', age: 30, address: { city: 'NY' } };
node src/index.js "Entity One" "Entity Two" "Entity Three" const b = { name: 'Alice', age: 31, address: { city: 'NY' } };
const c = { name: 'Alice', age: 30, address: { city: 'LA' } };
const differences = compare(a, b, c);
console.log(differences);
// [
// { key: 'age', values: [30, 31, 30] },
// { key: 'address.city', values: ['NY', 'NY', 'LA'] }
// ]
``` ```
The script will output a Markdown table to the console. ## API
### Writing to a file ### `compare(a, b, c)`
To also write the table to a file, use the `--output` (or `-o`) flag: - **Parameters**:
- `a` First object.
- `b` Second object.
- `c` Third object.
- **Returns**: `Array` Sorted array of difference objects.
## Testing
Run the test suite with:
```bash ```bash
node src/index.js "Entity One" "Entity Two" "Entity Three" --output comparison.md npm test
``` ```
The file `comparison.md` will contain the same table. The tests cover basic equality, toplevel differences, nested differences, and missing keys.
## Project Structure
- `src/index.js` Entry point; orchestrates argument parsing, API calls, and output.
- `src/compare.js` Handles API requests to Tavily and returns summaries.
- `src/markdown.js` Builds the Markdown table string.
- `package.json` Project metadata and dependencies.
- `README.md` Documentation.
## License ## License
MIT License MIT
+13 -7
View File
@@ -1,14 +1,20 @@
{ {
"name": "tavily-compare", "name": "compare-three-entities",
"version": "1.0.0", "version": "1.0.0",
"description": "Compare three entities using Tavily API and generate markdown table", "description": "A utility to compare three entities and report differences.",
"main": "src/index.js", "main": "src/index.js",
"type": "module",
"scripts": { "scripts": {
"start": "node src/index.js" "test": "jest"
}, },
"dependencies": { "keywords": [
"dotenv": "^16.4.5", "compare",
"node-fetch": "^3.3.2" "entities",
"deep-equal",
"difference"
],
"author": "Auto-generated",
"license": "MIT",
"devDependencies": {
"jest": "^29.7.0"
} }
} }
+61 -41
View File
@@ -1,51 +1,71 @@
import fetch from 'node-fetch'; /**
import dotenv from 'dotenv'; * Deep comparison of three objects.
* Returns an array of differences where at least two values differ.
* Each difference is an object:
* { key: 'path.to.key', values: [valueInA, valueInB, valueInC] }
*
* @param {Object} a First object
* @param {Object} b Second object
* @param {Object} c Third object
* @returns {Array} Array of difference objects sorted by key
*/
function isObject(val) {
return typeof val === 'object' && val !== null;
}
dotenv.config(); function deepEqual(a, b) {
if (a === b) return true;
export async function compareEntities(names) { if (isObject(a) && isObject(b)) {
const apiKey = process.env.TAVILY_API_KEY; const keysA = Object.keys(a);
if (!apiKey) { const keysB = Object.keys(b);
throw new Error('TAVILY_API_KEY environment variable is not set.'); if (keysA.length !== keysB.length) return false;
for (const key of keysA) {
if (!deepEqual(a[key], b[key])) return false;
}
return true;
} }
// Handles NaN
if (Number.isNaN(a) && Number.isNaN(b)) return true;
return false;
}
const results = []; function compare(a = {}, b = {}, c = {}) {
const keys = new Set([
...Object.keys(a),
...Object.keys(b),
...Object.keys(c),
]);
const diffs = [];
for (const name of names) { for (const key of keys) {
try { const valA = a[key];
const response = await fetch('https://api.tavily.com/search', { const valB = b[key];
method: 'POST', const valC = c[key];
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${apiKey}`
},
body: JSON.stringify({
query: name,
search_depth: 'basic',
max_results: 1
})
});
if (!response.ok) { if (isObject(valA) && isObject(valB) && isObject(valC)) {
console.error(`Failed to fetch data for "${name}". Status: ${response.status} ${response.statusText}`); const nestedDiffs = compare(valA, valB, valC);
results.push({ name, summary: 'Error fetching data' }); for (const nd of nestedDiffs) {
continue; diffs.push({
key: `${key}.${nd.key}`,
values: nd.values,
});
}
} else {
if (
!deepEqual(valA, valB) ||
!deepEqual(valA, valC) ||
!deepEqual(valB, valC)
) {
diffs.push({
key,
values: [valA, valB, valC],
});
} }
const data = await response.json();
const content = data.results && data.results[0] && data.results[0].content
? data.results[0].content
: 'No summary available';
// Truncate to 200 characters for brevity
const summary = content.length > 200 ? content.slice(0, 200) + '...' : content;
results.push({ name, summary });
} catch (err) {
console.error(`Error processing "${name}": ${err.message}`);
results.push({ name, summary: 'Error processing entity' });
} }
} }
return results; diffs.sort((d1, d2) => d1.key.localeCompare(d2.key));
return diffs;
} }
module.exports = compare;
+8 -50
View File
@@ -1,51 +1,9 @@
import { compareEntities } from './compare.js'; /**
import { generateMarkdownTable } from './markdown.js'; * Entry point for the comparison library.
import fs from 'fs'; * Exports the compare function as both named and default export.
import path from 'path'; */
const compare = require('./compare');
function parseArgs() { module.exports = compare;
const args = process.argv.slice(2); module.exports.default = compare;
const entities = []; module.exports.compare = compare;
let outputFile = null;
for (let i = 0; i < args.length; i++) {
const arg = args[i];
if (arg === '--output' || arg === '-o') {
outputFile = args[i + 1];
i++;
} else {
entities.push(arg);
}
}
return { entities, outputFile };
}
async function main() {
const { entities, outputFile } = parseArgs();
if (entities.length !== 3) {
console.error('Error: Exactly three entity names must be provided.');
console.error('Usage: node src/index.js <entity1> <entity2> <entity3> [--output <file.md>]');
process.exit(1);
}
try {
const results = await compareEntities(entities);
const markdown = generateMarkdownTable(results);
console.log('\nGenerated Markdown Table:\n');
console.log(markdown);
if (outputFile) {
const filePath = path.resolve(process.cwd(), outputFile);
fs.writeFileSync(filePath, markdown, 'utf-8');
console.log(`\nMarkdown table written to ${filePath}`);
}
} catch (err) {
console.error(`Fatal error: ${err.message}`);
process.exit(1);
}
}
main();
+37
View File
@@ -0,0 +1,37 @@
const compare = require('../src/compare');
describe('compare function', () => {
test('returns empty array when all objects are equal', () => {
const a = { x: 1, y: 2 };
const b = { x: 1, y: 2 };
const c = { x: 1, y: 2 };
expect(compare(a, b, c)).toEqual([]);
});
test('detects differences in top-level keys', () => {
const a = { x: 1, y: 2 };
const b = { x: 1, y: 3 };
const c = { x: 1, y: 2 };
expect(compare(a, b, c)).toEqual([
{ key: 'y', values: [2, 3, 2] },
]);
});
test('detects differences in nested objects', () => {
const a = { a: { b: 1 } };
const b = { a: { b: 2 } };
const c = { a: { b: 1 } };
expect(compare(a, b, c)).toEqual([
{ key: 'a.b', values: [1, 2, 1] },
]);
});
test('handles missing keys', () => {
const a = { x: 1 };
const b = { x: 1, y: 2 };
const c = { x: 1 };
expect(compare(a, b, c)).toEqual([
{ key: 'y', values: [undefined, 2, undefined] },
]);
});
});