{"id":36680184,"url":"https://github.com/raigu/ordered-lists-sync","last_synced_at":"2026-01-12T10:57:39.275Z","repository":{"id":45849421,"uuid":"398385420","full_name":"raigu/ordered-lists-sync","owner":"raigu","description":"Library for synchronizing ordered data with the minimum of insert and delete operations. Suitable for lage data sets in isolated environments","archived":false,"fork":false,"pushed_at":"2023-12-11T21:44:01.000Z","size":42,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-11-07T14:01:45.782Z","etag":null,"topics":["data","lists","ordering","sync","syncrhonization","update"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/raigu.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2021-08-20T19:50:03.000Z","updated_at":"2024-08-21T20:39:43.000Z","dependencies_parsed_at":"2023-12-11T23:13:49.221Z","dependency_job_id":"a9202cab-ff3a-4596-a810-f4b7fba08f9e","html_url":"https://github.com/raigu/ordered-lists-sync","commit_stats":{"total_commits":43,"total_committers":1,"mean_commits":43.0,"dds":0.0,"last_synced_commit":"ed764d9084a426cd9934c0910956174dab71bdab"},"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"purl":"pkg:github/raigu/ordered-lists-sync","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/raigu%2Fordered-lists-sync","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/raigu%2Fordered-lists-sync/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/raigu%2Fordered-lists-sync/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/raigu%2Fordered-lists-sync/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/raigu","download_url":"https://codeload.github.com/raigu/ordered-lists-sync/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/raigu%2Fordered-lists-sync/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28338887,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-12T10:40:25.642Z","status":"ssl_error","status_checked_at":"2026-01-12T10:39:27.820Z","response_time":98,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["data","lists","ordering","sync","syncrhonization","update"],"created_at":"2026-01-12T10:57:39.208Z","updated_at":"2026-01-12T10:57:39.265Z","avatar_url":"https://github.com/raigu.png","language":"PHP","readme":"[![Latest Stable Version](http://poser.pugx.org/raigu/ordered-lists-sync/v/stable)](https://packagist.org/packages/raigu/ordered-lists-sync)\n[![build](https://github.com/raigu/ordered-lists-sync/workflows/build/badge.svg)](https://github.com/raigu/ordered-data-sync/actions)\n[![codecov](https://codecov.io/gh/raigu/ordered-lists-sync/branch/main/graph/badge.svg?token=43B0X95CZ3)](https://codecov.io/gh/raigu/ordered-data-sync)\n[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/raigu/ordered-lists-sync/badges/quality-score.png?b=main)](https://scrutinizer-ci.com/g/raigu/ordered-lists-sync/?branch=main)\n[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)\n[![Dependents](http://poser.pugx.org/raigu/ordered-lists-sync/dependents)](https://packagist.org/packages/raigu/ordered-lists-sync)\n\nLibrary for synchronizing ordered data with the minimum of insert and delete operations. \n\nOptimized for large datasets. Suitable for keeping in sync internal data with outside source. See [demos](./demo).\n\n\n# Compatibility\n\n* PHP 7.4, 8.0, 8.1, 8.2, 8.3\n\n# Installations\n\n```bash\n$ composer require raigu/ordered-lists-sync\n```\n\n# Usage\n\n`\\Raigu\\OrderedListsSynchronization\\Synchronization` compares source and target lists. It detects which elements have\nbeen added or removed in source compared to the target and calls corresponding callback.\n\nThe source and target must be of type `Iterator`.\n\n```php\n$synchronization = new \\Raigu\\OrderedListsSynchronization\\Synchronization();\n$synchronization(\n    $source = new ArrayIterator(['A', 'B', 'D']), \n    $target = new ArrayIterator(['B', 'C', 'D']),\n    $add = function ($element) { echo \"ADD: {$element}\\n\"; },\n    $remove = function ($element) { echo \"REMOVE: {$element}\\n\"; }\n);\n```\n\nOutput:\n\n```\nADD: A\nREMOVE: C\n```\n\n# Use Cases\n\nSample use cases with demo code:\n\n* Keeping a relational database table in sync with another (\n  demo: [./demo/database_tables.php](./demo/database_tables.php))\n* Keeping local data in sync with data available in internet (\n  demo: [./demo/internet_to_database.php](./demo/internet_to_database.php))\n* Syncing entities identified by unique id and having several attributes  (demo: [./demo/sync_entities.php](./demo/sync_entities.php))\n\n# Design\n\nThe `\\Raigu\\OrderedListsSynchronization\\Synchronization` has only one purpose. Therefore, it\nis [designed](https://www.php.net/manual/en/language.oop5.magic.php#object.invoke) so the instance will be a function,\nno method is exposed. It is an object because it allows extending it in future (for example adding logging).\n\nUsing `Iterator` type for source and target has several advantages.\n\nFirst, you can create your own iterators as separate components which are easier to develop and test.\n\nSecondly, more complex problems can be solved. Specially if there are memory constraints.  \nYou can make source or target as [generator](https://www.php.net/manual/en/language.generators.overview.php), as an\ninstance implementing [Iterate](https://www.php.net/manual/en/class.iterator.php)\nor [IteratorAggregate](https://www.php.net/manual/en/class.iteratoraggregate.php) interface or use\n[Sandard PHP Library (SPL) iterators](https://www.php.net/manual/en/spl.iterators.php).\n\nThirdly, it allows creating components which do not do any job in constructor. This is convenient when using dependency\ninjection or declarative programming.\n\n# Development\n\n## Setting up\n\n```bash\n$ git clone git@github.com:raigu/ordered-lists-sync.git\n$ cd ordered-lists-sync\n$ composer install\n```\n\n## Testing\n\n```bash\n$ composer test\n$ composer coverage\n```\n\n\n\n# Algorithm\n\nAlgorithm works same way like we use dictionary. If all words are ordered, then we know exactly where some word should be.\nIf we have two dictionaries and start to compare them word by word from start, then we can detect added or removed words.\n\nExample (`V` denotes current position of the iterator):\n\n```text\n        V\nSource: A, B, D\n        V\nTarget: B, C, D\n```\n\nA \u003c B =\u003e if source is before target, then this means that value is missing in target. If there would be an A in target,\nthen it should be here before B. But it is not. Therefore, it is missing and should be added. Add A and move source\ncursor:\n\n```text\n           V\nSource: A, B, D\n        V\nTarget: B, C, D\n```\n\nB = B =\u003e if the source and target are equal, then they are sync. Move both cursors.\n\n```text\n              V\nSource: A, B, D\n           V\nTarget: B, C, D\n```\n\nD \u003e C =\u003e if source is after the target, then this means that value has been removed from source. Therefore, remove C and\nmove the cursor forward:\n\n```text\n              V\nSource: A, B, D\n              V\nTarget: B, C, D\n```\n\nD = D =\u003e they are in sync. Move both cursors. \n\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fraigu%2Fordered-lists-sync","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fraigu%2Fordered-lists-sync","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fraigu%2Fordered-lists-sync/lists"}