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)
- A Tavily API key
## Installation
## Setup
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
```
```bash
npm install compare-three-entities
```
## Usage
Run the script with three entity names:
```js
const compare = require('compare-three-entities');
```bash
node src/index.js "Entity One" "Entity Two" "Entity Three"
const a = { name: 'Alice', age: 30, address: { city: 'NY' } };
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
node src/index.js "Entity One" "Entity Two" "Entity Three" --output comparison.md
npm test
```
The file `comparison.md` will contain the same table.
## 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.
The tests cover basic equality, toplevel differences, nested differences, and missing keys.
## License
MIT License
MIT
+13 -7
View File
@@ -1,14 +1,20 @@
{
"name": "tavily-compare",
"name": "compare-three-entities",
"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",
"type": "module",
"scripts": {
"start": "node src/index.js"
"test": "jest"
},
"dependencies": {
"dotenv": "^16.4.5",
"node-fetch": "^3.3.2"
"keywords": [
"compare",
"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.
* 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();
export async function compareEntities(names) {
const apiKey = process.env.TAVILY_API_KEY;
if (!apiKey) {
throw new Error('TAVILY_API_KEY environment variable is not set.');
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;
}
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) {
try {
const response = await fetch('https://api.tavily.com/search', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${apiKey}`
},
body: JSON.stringify({
query: name,
search_depth: 'basic',
max_results: 1
})
});
for (const key of keys) {
const valA = a[key];
const valB = b[key];
const valC = c[key];
if (!response.ok) {
console.error(`Failed to fetch data for "${name}". Status: ${response.status} ${response.statusText}`);
results.push({ name, summary: 'Error fetching data' });
continue;
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],
});
}
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';
import fs from 'fs';
import path from 'path';
/**
* Entry point for the comparison library.
* Exports the compare function as both named and default export.
*/
const compare = require('./compare');
function parseArgs() {
const args = process.argv.slice(2);
const entities = [];
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();
module.exports = compare;
module.exports.default = compare;
module.exports.compare = compare;
+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] },
]);
});
});