Расчет взвешенных значений в виде целых чисел для макета - PullRequest
0 голосов
/ 01 марта 2019

У меня есть макет, в котором:

  • Страница разбита на столько фиксированной ширины столбцов , сколько может поместиться на ней.
  • Рядов ящики помещаются в эти столбцы , каждый из которых охватывает целое число столбцов .
  • Каждый ящик имеет используемый весопределить их диапазон столбцов (IE: если строка имеет 6 столбцов и 2 блоков с весами 1 и 2, блоков будет иметь интервалы столбцов 2 и4 соответственно).

Если бы допускались дробные интервалы столбцов, это было бы просто (умножить каждый вес на количество столбцов и разделить на общий вес), но разбить его на целые числа оказалось бы большесложно.Вот что я придумал:

function weightedIntegerValues(weights, total) {
    const totalWeight = totalValues(weights);
    const weightedValues = weights.map(weight => Math.round(total * weight / totalWeight));

    for (let totalValue = totalValues(weightedValues); totalValue > total; totalValue = totalValues(weightedValues))
        subtractOneFromHighest(weightedValues);

    return weightedValues;
}

Для краткости я опустил следующие функции:

  • totalValues - получает сумму всех значений вмассив
  • subtractOneFromHighest - находит наибольшее значение в массиве и вычитает 1 из него (изменяет массив на месте)

Функция работает следующим образом:

  1. Вычисляет взвешенные значения, как описано выше, но округляет каждое значение по мере его продвижения.
  2. Постоянно вычитает 1 из наибольшего значения в weightedValues, пока сумма weightedValues не станет меньше или равна * 1047.* (с учетом любых округленных до 0,5 пар)

Эта функция имеет две основные проблемы:

  1. Она ужасно неэффективна (как totalValues, так и * 1055).* должен пройти через массив внутри основного цикла функции)
  2. Это неправильно способствует уменьшению первого "наибольшего значения", которое он находит.

Для иллюстрации пункта (2) рассмотримследующее:

weightedIntegerValues([1, 2, 4, 3], 5); // [1, 1, 1, 2]

Найдена весовая функция rокругленные значения [1, 1, 2, 2], определяемые как превышающие желаемую сумму 5, и вычитаем 1 из первого наибольшего найденного значения (по индексу 3), но на самом деле нам хотелось бы вычесть 1 из индекса 4, который до этого был 1,5округляя, давая нам [1, 1, 2, 1].

Мои вопросы следующие:

  1. Можно ли это сделать лучше, чем N 2 ?Было бы здорово уменьшить его до N.
  2. Есть ли какой-нибудь простой и более математический способ отдавать предпочтение числам, округленным до 0,5, вместо того, чтобы отдавать предпочтение значениям lefter или правильнее?
  3. Есть какой-то аккуратный CSS, который может обработать этот вариант использования для меня?Flex-box подходит довольно близко, но пока что не совсем подходит для меня (возможно, это уже совсем другой вопрос).

1 Ответ

0 голосов
/ 04 марта 2019

Вот способ добиться этого смутно, основываясь на этом ответе:

function weightedIntegerValues(weights, total) {
    let flooredValues = [];
    const weightSum = weights.reduce((sum, item) => {
        return sum + valueFunc(item);
    }, 0);

    let flooredSum = 0

    weights.forEach((weight, i) => {
        const weighted = total * weight / weightSum
        const floored = Math.floor(weighted);

        flooredValues.push({
            floored: floored,
            diff: weighted - floored,
            index: i
        });

        flooredSum = flooredSum + flooredValues[i].floored;
    });

    flooredValues = flooredValues.sort((a, b) => b.diff - a.diff);

    const difference = total - flooredSum;

    for (let i = 0; i < difference; i++) {
        flooredValues[i].floored += 1;
    }

    return flooredValues.sort((a, b) => a.index - b.index).map(v => v.floored);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...