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
+33 -40
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
1. **Clone the repository** (or copy the files into a new directory):
```bash ```bash
git clone https://github.com/your-username/tavily-compare.git npm install compare-three-entities
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"
} }
} }
+62 -42
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.
dotenv.config(); * Each difference is an object:
* { key: 'path.to.key', values: [valueInA, valueInB, valueInC] }
export async function compareEntities(names) { *
const apiKey = process.env.TAVILY_API_KEY; * @param {Object} a First object
if (!apiKey) { * @param {Object} b Second object
throw new Error('TAVILY_API_KEY environment variable is not set.'); * @param {Object} c Third object
* @returns {Array} Array of difference objects sorted by key
*/
function isObject(val) {
return typeof val === 'object' && val !== null;
} }
const results = []; function deepEqual(a, b) {
if (a === b) return true;
if (isObject(a) && isObject(b)) {
const keysA = Object.keys(a);
const keysB = Object.keys(b);
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;
}
for (const name of names) { function compare(a = {}, b = {}, c = {}) {
try { const keys = new Set([
const response = await fetch('https://api.tavily.com/search', { ...Object.keys(a),
method: 'POST', ...Object.keys(b),
headers: { ...Object.keys(c),
'Content-Type': 'application/json', ]);
'Authorization': `Bearer ${apiKey}` const diffs = [];
},
body: JSON.stringify({ for (const key of keys) {
query: name, const valA = a[key];
search_depth: 'basic', const valB = b[key];
max_results: 1 const valC = c[key];
})
if (isObject(valA) && isObject(valB) && isObject(valC)) {
const nestedDiffs = compare(valA, valB, valC);
for (const nd of nestedDiffs) {
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],
}); });
if (!response.ok) {
console.error(`Failed to fetch data for "${name}". Status: ${response.status} ${response.statusText}`);
results.push({ name, summary: 'Error fetching data' });
continue;
} }
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] },
]);
});
});