Наверно, многим уже приходилось решать такую задачу, но я с ней столкнулся лишь недавно. В общем, на входе есть некое множество уникальных значений, допустим, {0, 1, 3, 7, 9, 15, 20}, их
последовательно нужно заменить на значения из другого множества, скажем, {1, 2, 3, 4, 5, 6, 7}, но так, чтобы не нарушалась уникальность значений результирующего множества, т.е. чтобы замена ни на каком шаге не приводила к появлению дубликатов (оба множества могут частично пересекаться). Пример из жизни: замена определенным образом сгенерированных значений поля на значения, сгенерированные иным образом - при условии, что по этому полю существует уникальный индекс. По ходу дела получился нижеприведенный метод, возвращающий список с парами значений для замены в нужном порядке.
X++:
/// <summary>
/// возвращает список пар [исходное значение, новое значение] для последовательной замены исходных значений на
/// новые таким образом, чтобы на любом шаге замены в изменяемом множестве значений не возникало дубликатов
/// может использоваться, к примеру, для перебивки значений поля таблицы, на котором висит уникальный индекс
/// </summary>
/// <param name="_setOfValues2Replace">
/// множество значений, подлежащих замене
/// должно быть непустым и иметь простой значимый (не ссылочный) базовый тип
/// </param>
/// <param name="_setOfNewValues">
/// множество новых значений, на которые надо заменить исходные
/// может полностью или частично пересекаться со значениями, подлежащими замене
/// должно иметь тот же базовый тип и число элементов, что и _setOfValues2Replace
/// </param>
/// <returns>
/// список контейнеров [исходное значение, новое значение] для последовательной замены в нужном порядке
/// либо пустой список, если замена невозможна (к примеру, множества исходных и новых значений тождественны)
/// </returns>
/// <remarks>
/// ACHTUNG!!! исходные множества изменяются!
/// </remarks>
/// <exception cref="Exception::Error">
/// выбрасывается, если входные параметры не соответствуют предусловиям
/// </exception>
public static client server List DEV_getListOfValueReplacementPairs(Set _setOfValues2Replace, Set _setOfNewValues)
{
Set setOfUniqueNewValues;
Set setOfCommonValues;
SetIterator setIterNew;
SetIterator setIterOld;
anytype oldValue;
anytype newValue;
Types baseType;
Counter nElements;
List ret;
;
if (_setOfValues2Replace)
{
baseType = _setOfValues2Replace.typeId();
nElements = _setOfValues2Replace.elements();
}
// проверка предусловий
if (!( _setOfNewValues
&& _setOfValues2Replace
&& nElements > 0
&& nElements == _setOfNewValues.elements()
&& baseType == _setOfNewValues.typeId()
&& ( baseType == Types::Date
|| baseType == Types::Enum
|| baseType == Types::Guid
|| baseType == Types::Int64
|| baseType == Types::Integer
|| baseType == Types::Real
|| baseType == Types::RString
|| baseType == Types::String
|| baseType == Types::Time
|| baseType == Types::UtcDateTime
|| baseType == Types::VarString
)
))
{
throw error( Error::wrongUseOfFunction( funcname() ) );
}
ret = new List( Types::Container );
setOfCommonValues = Set::intersection( _setOfValues2Replace, _setOfNewValues );
if (setOfCommonValues.elements() < nElements)
{
if (!setOfCommonValues.empty())
{
setOfUniqueNewValues = Set::difference( _setOfNewValues, setOfCommonValues );
setIterNew = new SetIterator( setOfUniqueNewValues );
setIterOld = new SetIterator( setOfCommonValues );
while (setIterNew.more())
{
if (!setIterOld.more())
{
break;
}
oldValue = setIterOld.value();
newValue = setIterNew.value();
ret.addEnd( [ oldValue, newValue ] );
_setOfNewValues.remove( newValue );
_setOfValues2Replace.remove( oldValue );
setOfUniqueNewValues.remove( newValue );
if (_setOfNewValues.in( oldValue ))
{
setOfUniqueNewValues.add( oldValue );
setIterNew.begin();
}
else
{
setIterNew.next();
}
setIterOld.next();
}
Debug::assert( _setOfValues2Replace.elements() == setOfUniqueNewValues.elements() );
}
else
{
setIterNew = new SetIterator( _setOfNewValues );
}
// оставшиеся на замену значения никак не пересекаются с новыми
setIterNew.begin();
setIterOld = new SetIterator( _setOfValues2Replace );
while (setIterNew.more() && setIterOld.more())
{
oldValue = setIterOld.value();
newValue = setIterNew.value();
ret.addEnd( [ oldValue, newValue ] );
setIterNew.next();
setIterOld.next();
}
Debug::assert( ret.elements() == nElements );
}
return ret;
}