Структуры данных, полученные из файлов YAML, обычно содержат ключи со значениями, являющимися ссылками на массивы с хэш-ссылками. В вашем тестовом случае это ссылка на массив для ключа test
.
Тогда такой инструмент, как Hash::Merge
, может добавлять только хеш-ссылки к массиву ссылок, принадлежащих тому же ключу; он не предназначен для сравнения элементов массива. Поэтому вам нужно сделать это самостоятельно, чтобы удалить дубликаты или применить к данным какие-либо определенные правила по вашему выбору.
Один из способов справиться с этим - сериализовать (то есть упорядочить) сложные структуры данных в каждой ссылке на массив, которые могут содержать дубликаты, чтобы можно было построить хэш с ними, являющимися ключами, что является стандартным способом обработки дубликатов (со сложностью O (1) , хотя, возможно, и с большой константой).
В Perl существует несколько способов сериализации данных. Я бы рекомендовал JSON::XS как очень быстрый инструмент с выводом, который можно используется любым языком и инструментом. (Но, конечно, изучите другие, которые могут лучше соответствовать вашим конкретным потребностям.)
Простой полный пример с использованием ваших тестовых случаев
use strict;
use warnings;
use feature 'say';
use Data::Dump qw(dd pp);
use YAML;
use JSON::XS;
use Hash::Merge qw( merge );
#Hash::Merge::set_behavior('RETAINMENT_PRECEDENT'); # irrelevant here
die "Usage: $0 in-file1 in-file2 output-file-name\n" if @ARGV != 3;
my ($yaml1, $yaml2, $yaml_out) = @ARGV;
my $hr1 = YAML::LoadFile($yaml1);
my $hr2 = YAML::LoadFile($yaml2);
my $merged = merge($hr2, $hr1);
#say "merged: ", pp $merged;
for my $key (keys %$merged) {
# The same keys get overwritten
my %uniq = map { encode_json $_ => 1 } @{$merged->{$key}};
# Overwrite the arrayref with the one without dupes
$merged->{$key} = [ map { decode_json $_ } keys %uniq ];
}
dd $merged;
# Save the final structure...
Более сложные структуры данных требуют более разумного обхода; рассмотрите возможность использования инструмента для этого.
С файлами, как показано в вопросе, это печатает
{
test => [
{ directory => "LIB_DIR", name => "ObsSel.ktc", project => "TOT" },
{ directory => "MODEL_DIR", name => "pipe.v", project => "TOT" },
{
directory => "PCIE_LIB_DIR",
name => "pciechip.ktc",
project => "PCIE_MODE",
},
{ directory => "NAME_DIR", name => "fame.v", project => "SINGH" },
{ directory => "TREE_PROJECT", name => "Syn.yml", project => "TOT" },
],
}
(Я использую Data::Dump для отображения сложных данных из-за их простоты и компактного вывода по умолчанию. .)
Если есть проблемы с сериализацией и сравнением целых структур, рассмотрите возможность использования какого-либо дайджеста (контрольной суммы, хеширования).
Другим вариантом было бы сравнение структур данных в том виде, в котором они есть, для устранения дубликатов вручную. Для сравнения сложных структур данных мне нравится использовать Test::More
, который очень хорошо работает для простых сравнений вне какого-либо тестирования. Но, конечно, есть и специальные инструменты, такие как Data::Compare
.
Наконец, вместо ручной обработки результата наивного merge
, как описано выше, можно закодировать желаемое поведение, используя Hash::Merge::add_behavior_spec и затем пусть все это сделает модуль. Конкретные примеры использования этой функции см., например, в этой публикации и этот пост.
Обратите внимание, что в этом случае вы по-прежнему пишете весь код для выполнения работы, как описано выше, но модуль снимает с вас часть механики.
person
zdim
schedule
20.05.2021