根据现有值在数组中创建新元素

时间:2017-01-25 15:14:34

标签: php arrays

我有以下数组,其中包含一组句点:

Array
(
    Array
    (
        [period_start] => 1
        [period_end] => 12
    )

    Array
    (
        [period_start] => 4
        [period_end] => 8
    )

)

我想分割与其他时期重叠的句号。例如,因为第二个句点与第一个句点重叠,所以它应该将第一个句点分成两个句点,使它看起来像这样:

Array
(
    Array
    (
        [period_start] => 1
        [period_end] => 3
    )

    Array
    (
        [period_start] => 4
        [period_end] => 8
    )

    Array
    (
        [period_start] => 9
        [period_end] => 12
    )

)

因此,没有两个句点包含另一个句点范围内的开始和结束值。但我不知道如何以有效的方式实现这一目标。任何帮助将不胜感激。

编辑:对于评论,这篇文章更多的是对橡皮鸭的恳求,而不是让别人为我做我的工作。我已经解决了我的问题(支持你自己):

// Sort the collection by period_start in ascending order.
function sortByPeriod(&$collection) {
    usort($collection, function ($value1, $value2) {
        if (!array_key_exists('period_start', $value1) || !array_key_exists('period_start', $value2)) {
            return 0;
        }

        if ($value1['period_start'] == $value2['period_start']) {
            return 0;
        }

        return $value1['period_start'] < $value2['period_start'] ? -1 : 1;
    });
}

$periods = array();

$products = array(
    array(
        'period_start' => 4,
        'period_end' => 8
    ),
    array(
        'period_start' => 1,
        'period_end' => 12
    )
);

sortByPeriod($products);

foreach ($products as $product) {
    // Store them in $periods using a key, so that if an identical period comes along on a future iteration, it doesn't get counted. The keys aren't required.
    if (array_key_exists('period_start', $product) && !is_null($product['period_start'])) {
        if (!array_key_exists($product['period_start'] . '-' . $product['period_end'], $periods)) {
            $productStart = $product['period_start'];
            $productEnd   = $product['period_end'];

            // Go through each period already inserted
            foreach ($periods as &$period) {
                $periodStart = $period['period_start'];
                $periodEnd   = $period['period_end'];

                If the product's start overlaps the period's end
                if ($productStart <= $periodEnd) {
                    // Set that period's end to the product's start - 1
                    $period['period_end'] = $productStart - 1;

                    // If the overlapping product is entirely within the period (e.g. period is 1-12, product is 4-8, like the example provided earlier)   
                    if ($productEnd <= $periodEnd) {
                        // Add a new period, whose start is the product's end + 1 and the end is the initial period's end.
                        $periods[($productEnd + 1) . '-' . $periodEnd] = array(
                            'period_start' => $productEnd + 1,
                            'period_end'   => $periodEnd
                        );
                    // The product's period isn't entirely within the period (e.g. period is 1-6, product is 4-8)
                    } else {
                        // Add a new period from product start to period end (e.g. following the example above, the period becomes 1-3, insert 4-6)
                        $periods[$productStart . '-' . $periodEnd] = array(
                            'period_start' => $productStart,
                            'period_end'   => $periodEnd
                        );
                        // Set the product's start to the period's end + 1 (e.g. 7)
                        $productStart = $periodEnd + 1;
                    }
                }
            }
            // Add the period (following the example iteration above, product start = 7, end = 8)
            $periods[$productStart . '-' . $productEnd] = array(
                'period_start' => $productStart,
                'period_end'   => $productEnd
            );
        }
    }

    // After one iteration, we have 1-3, 4-6 and 7-8
}

sortByPeriod($periods);

$periods = array_values($periods);
print_r($periods);

如上所示,它可以产生并产生预期的输出。但是,正如您所看到的,它组织得不是很好,我觉得有更好的方法可以解决这个问题。

谢谢。

1 个答案:

答案 0 :(得分:0)

我意识到我是以一种奇怪的方式解决这个问题。我的想法是,如果我有一个句点范围(让我们说1-8)和另一个(4-12),它应该将这些范围拆分并留给1-3, 4-8, 9-12。虽然输出确实是我想要的输出,但是通过将其分开并添加新的周期以补偿其间的缺失范围来实现它是太复杂了。我是这样想的(我让它以这种方式工作):

  我有三条面包,每条面包都有不同的大小。我需要把它们全部分开,这样我就可以吃几块面包,足以等于最大面包的内容。好吧,让我们从这块面包中取出一块,从面包中取出一块,然后将它们放在一起。

一切都很糟糕。真的,最好的方法是使用最大的面包并切割其他面包适合的确切位置,所以不要使用三个面包,我只使用一个。

我将理论付诸实践(使用JavaScript。我可以将其移植到PHP)。首先,我需要一种方法让每个时期都难以区分,因此,如果我的初始时期与1-3, 1-12 and 4-8一致,则会将1-34-8计为两个不同的时段。幸运的是,我的所有商品都是具有正常价格和折扣价的产品。折扣价在折扣期间生效。

接下来,我需要确定最大的时期并记下它的开始和结束时间:

var range = {};

this.products.forEach(product => {
    if (!range.start) {
        range.start = product.start;
    }

    if (!range.end) {
        range.end = product.end;
    }

    if (product.start < range.start) {
        range.start = product.start
    }

    if (product.end > range.end) {
        range.end = product.end;
    }
});

现在,对于每个产品,我每次迭代从头到尾迭代1次,如果产品的期间在当前迭代范围内,则存储产品的折扣价,否则它是正常的价:

var periodCounter = [];

this.products.forEach(product => {
    for (i = range.start; i <= range.end; i ++) {
        if (!periodCounter[i]) {
            periodCounter[i] = 0;
        }

        if (i >= product.start && i <= product.end) {
            periodCounter[i] += product.def_amount;
        } else {
            periodCounter[i] += product.amount;
        }
    }
});

大。现在我有一个阵列,在每个月的一个月内都填写了所有产品的价格。现在我需要确定实际的时期。这非常简单 - 遍历数组,检查当前价格是否等于之前的价格。如果是,我们仍然处于一个时期。如果不是,我们已经到了那个时期的末尾并开始了一个新的时间:

var periods = [];
var periodStart = 0;
var periodEnd = 0;

for (i = range.start; i <= range.end; i ++) {
    if (i == range.start) {
        periodStart = i;
    } else {
        if (periodCounter[i] != periodCounter[i-1]) {
            periodEnd = i-1;
            periods.push({
                start: periodStart,
                end: periodEnd,
                amount: periodCounter[i-1]
            });
            periodStart = i;
        } 

        if (i == range.end) {
            periods.push({
                start: periodStart,
                end: i,
                amount: periodCounter[i]
            });
        }
    }
}

如果有任何机会总共两个&#34;逻辑&#34;期间等于相同的价格,它并不重要。最终用户只需知道这些时期的价格是什么,他们在知道两个时期相同的价格时没有获得任何信息,所以从本质上讲,你可能只是将这些时期连接成一个大的时期,这就是发生。如果绝对有必要显示真正的&#34;逻辑&#34;句点,而不是在计数器中存储价格,使用字节值(1,2,4,8等)。

我为这个项目制作了一个codepen,使用Vue.js和Bulma来显示产品和正确的句点。当然,我可以采取更好的方式来解决这个问题。