Показать сообщение отдельно
Старый 23.11.2010, 00:31   #1  
gl00mie is offline
gl00mie
Участник
MCBMSS
Most Valuable Professional
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
3,684 / 5788 (200) ++++++++++
Регистрация: 28.11.2005
Адрес: Москва
Записей в блоге: 3
Последовательная замена множества уникальных значений на другие без возникновения дубликатов
Наверно, многим уже приходилось решать такую задачу, но я с ней столкнулся лишь недавно. В общем, на входе есть некое множество уникальных значений, допустим, {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;
}

Последний раз редактировалось gl00mie; 23.11.2010 в 01:52. Причина: typo...
За это сообщение автора поблагодарили: mazzy (5), Wamr (3).