Related
I would like you to help me with an algorithm in PHP that can loop through an array of values, sort them and print non-duplicates.
Here is the code I wrote. I want to do it with only loop and if else statement. I would appreciate any help in achieving this. Thanks
$input_array = [3, 5, 7, 7, 8, 3, 1, 9, 9, 9, 0, 2, 4, 8, 0, 12, 5, 8, 2];`
$count_array = count($input_array); // count the array`enter code here`
for($i = 0; $i < $count_array; $i++){ //loop through the array 1st time
$check_array = false;
//sort array
for($j = $i+1; $j < $count_array; $j++){
if($input_array[$i] > $input_array[$j]){
$non_duplicates = $input_array[$i];
$input_array[$i] = $input_array[$j];
$input_array[$j] = $non_duplicates;
}
else if($input_array[$i] == $input_array[$j] &&( $i != $j)){
$check_array = true;
break;
}
else{
$input_array[$i] != $input_array[$j];
}
}
if(!$check_array){
echo($input_array[$i]. ', ');
}
}
You can do it with 2 for cycles where the first cycle is for the first element and the 2nd cycle is always in the next position, this will help to always check if the first number is less than the second. You create a temporary variable where you store the value of the first number and you pass the value of the second number to the variable of the first one, later the temporary one that you had stored you pass it to the variable of the second number (with this you got to invert the values of one to the other).
As this is ordering them, later an if is made where it is verified if they are equal, in case of being equal a unset() is made to eliminate that data of the array.
// Array of values
$input_array = [3, 5, 7, 7, 8, 3, 1, 9, 9, 9, 0, 2, 4, 8, 0, 12, 5, 8, 2];
// count the length of array
$count = count($input_array);
// order array and remove duplicates
for ($i = 0; $i < $count; $i++) {
for ($j = $i + 1; $j < $count; $j++) {
// order array
if ($input_array[$i] < $input_array[$j]) {
$temp = $input_array[$i];
$input_array[$i] = $input_array[$j];
$input_array[$j] = $temp;
}
// delete duplicates
if ($input_array[$i] == $input_array[$j]) {
unset($input_array[$j]);
}
}
}
// Return an array with elements in reverse order
$input_array = array_reverse($input_array);
You get something like this:
Dump => array(9) {
[0] => int(1)
[1] => int(2)
[2] => int(3)
[3] => int(4)
[4] => int(7)
[5] => int(5)
[6] => int(8)
[7] => int(9)
[8] => int(12)
}
I have an array of posts ids and an array of videos ids. I need to mix both to have something like "First is a video, then 3 normal posts then another video then 5 normal posts.. repeat... until the end. (ie : Video,normal,normal,normal,Video,normal*5 and repeat..)
My code works but how can i make it more clean and concise ?
$a1 = $video_posts_arr;
$a2 = $standard_posts_arr;
$res = [];
$tc1 = count($a1);
$tc2 = count($a2);
$tc = $tc1 + $tc2;
$i1 = 0;
$i2 = 0;
for ($i = 0; $i < $tc; $i++) {
// first video
if (isset( $a1[$i1] )) {
array_push($res,$a1[$i1]);
$i1++;
}
// next 3 normals
if (isset( $a2[$i2]) ) {
array_push($res,$a2[$i2]);
$i2++;
array_push($res,$a2[$i2]);
$i2++;
array_push($res,$a2[$i2]);
$i2++;
}
// next video
if (isset( $a1[$i1] )) {
array_push($res,$a1[$i1]);
$i1++;
}
// next 5 normals
if (isset( $a2[$i2]) ) {
array_push($res,$a2[$i2]);
$i2++;
array_push($res,$a2[$i2]);
$i2++;
array_push($res,$a2[$i2]);
$i2++;
array_push($res,$a2[$i2]);
$i2++;
array_push($res,$a2[$i2]);
$i2++;
}
}```
It's kind of hard to tell from your wording, but I'm assuming you're wanting to go back and forth between 3 "regular" posts, then 5, then 3, etc, between each "video" post. So as we loop through the video posts, we have to toggle back and forth between those options, and then use that plus a marker/counter to know where to insert each video post in the list of regular posts.
$a1 = array("v1", "v2", "v3", "v4", "v5");
$a2 = array("p1", "p2", "p3", "p4", "p5", "p6", "p7", "p8", "p9", "p10", "p11", "p12", "p13", "p14", "p15", "p16");
$skipNum = 3;
$currentPos = 1;
array_unshift($a2, array_shift($a1));
foreach($a1 as $v)
{
$currentPos+= $skipNum;
array_splice($a2, $currentPos, 0, $v);
$currentPos++;
if($skipNum == 3)
{
$skipNum = 5;
}
else
{
$skipNum = 3;
}
}
var_dump($a2);
DEMO
how can i make it more clean and concise ?
Here's what I think you should do...
Iterate on the video array and inject each video element into the standard post array at the variable positions without making any replacements.
Code: (Demo)
$video_posts_arr = range('a','e');
$standard_posts_arr = range(1, 16);
$pos = 0;
foreach ($video_posts_arr as $index => $video) {
array_splice($standard_posts_arr, $index + $pos, 0, $video);
$pos += $index & 1 ? 5 : 3;
}
var_export($standard_posts_arr);
Using the $index of the videos array spares having to use incrementation while iterating.
I am using a ternary expression with a bitwise condition to increase the $pos based on whether $index is odd. I should express these variables to better explain what is happening. As shown in this Demo, these are the values generated as $index increases:
$index = 0, $pos = 0; splicePoint = 0, isOdd = false
$index = 1, $pos = 3; splicePoint = 4, isOdd = true
$index = 2, $pos = 8; splicePoint = 10, isOdd = false
$index = 3, $pos = 11; splicePoint = 14, isOdd = true
$index = 4, $pos = 16; splicePoint = 20, isOdd = false
Output:
array (
0 => 'a',
1 => 1,
2 => 2,
3 => 3,
4 => 'b',
5 => 4,
6 => 5,
7 => 6,
8 => 7,
9 => 8,
10 => 'c',
11 => 9,
12 => 10,
13 => 11,
14 => 'd',
15 => 12,
16 => 13,
17 => 14,
18 => 15,
19 => 16,
20 => 'e',
)
The above script is demonstrated using a ratio of videos to standard posts which results in no consecutive videos.
If your video to standard post ratio is too great, then the above script will simply append all remaining/extra videos to the end of the standard posts array.
If you would like to ensure that the final element in your standard posts array is not a video, then you can add a conditional break like this:
$pos = 0;
foreach ($video_posts_arr as $index => $video) {
$injection_pos = $index + $pos;
if (!isset($standard_posts_arr[$injection_pos])) {
break;
}
array_splice($standard_posts_arr, $injection_pos, 0, $video);
$pos += $index & 1 ? 5 : 3;
}
Or to allow a maximum of one video after the last standard post, move the break after the array_splice() call and account for the injected video element like this:
$pos = 0;
foreach ($video_posts_arr as $index => $video) {
$injection_pos = $index + $pos;
array_splice($standard_posts_arr, $injection_pos, 0, $video);
if (!isset($standard_posts_arr[$injection_pos + 1])) {
break;
}
$pos += $index & 1 ? 5 : 3;
}
Maybe something like that?
This solution uses array_merge and array_slice to create result. This methods does not edit original arrays, just creates new one based on arguments.
$a1 = array("v1", "v2", "v3");
$a2 = array("a1", "a2", "a3", "a4", "a5", "a6", "a7", "a8", "a9", "a10");
$output = array_merge(
array_slice($a1, 0, 1), //take one arg from a1. Not smartest to be honest but works. Maybe someone will give better idea here.
array_slice($a2, 0, 3), //take first 3 elements from a2
array_slice($a1, 1, 1),
array_slice($a2, 3, 5),
array_slice($a2, 300, 500), //it is save :)
);
print_r($output);
# [0] => v1
# [1] => a1
# [2] => a2
# [3] => a3
# [4] => v2
# [5] => a4
# [6] => a5
# [7] => a6
# [8] => a7
# [9] => a8
# [10] => a9
# [11] => a10
array_slice that takes only one argument array_slice($a1, 0, 1) looks bit cryptic but at least it is save to use.
This'll create new array instead of editing existing one. It comes with some additional overhead (You probably should not worry about it).
http://sandbox.onlinephpfunctions.com/code/9b23381b31ab7ed48f1941cbca981071eaf250af
I have an array with some values. Here I need to make a calculation for Year 2019 only.
$array = array(
"date_2019_12" => 0,
"date_2019_11" => 0,
"date_2019_10" => 0,
"date_2019_09" => 0,
"date_2019_08" => 0,
"date_2019_07" => 0,
"date_2019_06" => 0,
"date_2019_05" => 0,
"date_2019_04" => 0,
"date_2019_03" => 0,
"date_2019_02" => 0,
"date_2019_01" => 10,
"date_2018_12" => 1,
"date_2018_11" => 2,
"date_2018_10" => 3,
"date_2018_09" => 4,
"date_2018_08" => 5,
"date_2018_07" => 6,
);
krsort($array);
From this array, I need to calculate the sum of previous five months & current month starting "date_2019_01".
That means I need final array result like below:
"date_2019_01" => 25(10+1+2+3+4+5)
"date_2019_02" => 20(0+10+1+2+3+4)
"date_2019_03" => 16(0+0+10+1+2+3)
"date_2019_04" => 13(0+0+0+10+1+2)
... until "date_2019-12".
I have spend many times finding solution but I could not. Can anybody help me?
I have tried following code and somewhere now I am lost.
$newArr = array();
foreach($array as $key => $val) {
$explode = explode("_", $key);
$value = (int)$explode[2];
for($i = 0; $i <= 5; $i++) {
$newArr[$array[$explode[0].'_'.$explode[1].'_'.$value]] = $array[$explode[0].'_'.$explode[1].'_'.$value];
$value--;
}
}
You don't need to nest the loops, you can use one loop, array_slice and array_sum.
krsort($array);
$year = "2019";
$keys = array_keys($array); //save keys since we use array_values in the loop
foreach(array_values($array) as $k => $v){
// If the year is found in the key slice out the next six item and sum them
if(strpos($keys[$k], $year) !== false) $res[$keys[$k]] = array_sum(array_slice($array, $k, 6));
}
var_dump($res);
https://3v4l.org/LSQRg
This code will do what you want. It uses a nested loop to iterate over each month and the 5 preceding months, creating a sum for each one from the different date values in the array:
$year = 2019;
$sums = array();
for ($i = 1; $i <= 12; $i++) {
$sum = 0;
for ($j = $i - 5; $j <= $i; $j++) {
$y = $year;
$m = $j;
if ($m <= 0) {
$m += 12;
$y -= 1;
}
$date = sprintf("date_%4d_%02d", $y, $m);
$sum += $array[$date];
}
$date = sprintf("date_%4d_%02d", $year, $i);
$sums[$date] = $sum;
}
print_r($sums);
Output:
Array (
[date_2019_01] => 25
[date_2019_02] => 20
[date_2019_03] => 16
[date_2019_04] => 13
[date_2019_05] => 11
[date_2019_06] => 10
[date_2019_07] => 0
[date_2019_08] => 0
[date_2019_09] => 0
[date_2019_10] => 0
[date_2019_11] => 0
[date_2019_12] => 0
)
Demo on 3v4l.org
Use: https://3v4l.org/7AYfT
$newArr = array();
foreach($array as $key => $val) {
$i=1;
$newArr[$key] = $val;
foreach($array as $key2 => $val2){
if($key>$key2 && $i <= 5){
$newArr[$key] += $val2;
$i++;
}
}
}
array(18) {
["date_2019_12"]=>
int(0)
["date_2019_11"]=>
int(0)
["date_2019_10"]=>
int(0)
["date_2019_09"]=>
int(0)
["date_2019_08"]=>
int(0)
["date_2019_07"]=>
int(0)
["date_2019_06"]=>
int(10)
["date_2019_05"]=>
int(11)
["date_2019_04"]=>
int(13)
["date_2019_03"]=>
int(16)
["date_2019_02"]=>
int(20)
["date_2019_01"]=>
int(25)
["date_2018_12"]=>
int(21)
["date_2018_11"]=>
int(20)
["date_2018_10"]=>
int(18)
["date_2018_09"]=>
int(15)
["date_2018_08"]=>
int(11)
["date_2018_07"]=>
int(6)
}
There are two steps I have performed.
Step 1: Sorting custom format array by keys
// sort the array first by date of given format
uksort($array, function ($a, $b) {
$t1 = strtotime(str_replace(["date_", "_"], ["", "-"], $a) . '-01');
$t2 = strtotime(str_replace(["date_", "_"], ["", "-"], $b) . '-01');
return $t1 - $t2;
});
Step 2: Main logic to given condition with inline documentation
$flag = false;
$result = [];
foreach ($array as $key => $value) {
if ($key != 'date_2019_01' && !$flag) {
continue; // check until 'date_2019_01' wont come
} else {
$flag = true; // set the flag and skip above condition
$curKey = array_search($key, array_keys($array), true); // get integer index of date_2019_01
if ($key !== false) { // if key exists
$slice = array_slice($array, $curKey - 5, 6, true); // from current index last 5(6-5, 7-5,8-5,....) to 6(including current element)
$result[$key] = array_sum($slice); // sum of there values
}
}
}
Brief of things I have used to make it clear for understanding of applications of it.
uksort — Sort an array by keys using a user-defined comparison function
array_keys — Return all the keys or a subset of the keys of an array
array_search — Searches the array for a given value and returns the first corresponding key if successful
array_slice — Extract a slice of the array
array_sum — Calculate the sum of values in an array
str_replace — Replace all occurrences of the search string with the replacement string
Using PHP, I'd like to convert a string containing a Roman number into its integer representation. I need this because I need to make calculations on them.
Wikipedia on Roman numerals
It would suffice to only recognize the basic Roman numeral characters, like:
$roman_values=array(
'I' => 1,
'V' => 5,
'X' => 10,
'L' => 50,
'C' => 100,
'D' => 500,
'M' => 1000,
);
That means the highest possible number is 3999 (MMMCMXCIX). I will use N to represent zero, other than that only positive integers are supported.
I cannot use the PEAR library for Roman numbers.
I found this great question on SO on how to test whether the string contains a valid Roman numeral:
How do you match only valid roman numerals with a regular expression?
What would be the best way of coding this?
How about this:
$romans = array(
'M' => 1000,
'CM' => 900,
'D' => 500,
'CD' => 400,
'C' => 100,
'XC' => 90,
'L' => 50,
'XL' => 40,
'X' => 10,
'IX' => 9,
'V' => 5,
'IV' => 4,
'I' => 1,
);
$roman = 'MMMCMXCIX';
$result = 0;
foreach ($romans as $key => $value) {
while (strpos($roman, $key) === 0) {
$result += $value;
$roman = substr($roman, strlen($key));
}
}
echo $result;
which should output 3999 for the supplied $roman. It seems to work for my limited testing:
MCMXC = 1990
MM = 2000
MMXI = 2011
MCMLXXV = 1975
You might want to do some validation first as well :-)
I am not sure whether you've got ZF or not, but in case you (or any of you who's reading this) do here is my snippet:
$number = new Zend_Measure_Number('MCMLXXV', Zend_Measure_Number::ROMAN);
$number->convertTo (Zend_Measure_Number::DECIMAL);
echo $number->getValue();
Zend_Measure_Number on framework.zend.com
This is the one I came up with, I added the validity check as well.
class RomanNumber {
//array of roman values
public static $roman_values=array(
'I' => 1, 'V' => 5,
'X' => 10, 'L' => 50,
'C' => 100, 'D' => 500,
'M' => 1000,
);
//values that should evaluate as 0
public static $roman_zero=array('N', 'nulla');
//Regex - checking for valid Roman numerals
public static $roman_regex='/^M{0,3}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})$/';
//Roman numeral validation function - is the string a valid Roman Number?
static function IsRomanNumber($roman) {
return preg_match(self::$roman_regex, $roman) > 0;
}
//Conversion: Roman Numeral to Integer
static function Roman2Int ($roman) {
//checking for zero values
if (in_array($roman, self::$roman_zero)) {
return 0;
}
//validating string
if (!self::IsRomanNumber($roman)) {
return false;
}
$values=self::$roman_values;
$result = 0;
//iterating through characters LTR
for ($i = 0, $length = strlen($roman); $i < $length; $i++) {
//getting value of current char
$value = $values[$roman[$i]];
//getting value of next char - null if there is no next char
$nextvalue = !isset($roman[$i + 1]) ? null : $values[$roman[$i + 1]];
//adding/subtracting value from result based on $nextvalue
$result += (!is_null($nextvalue) && $nextvalue > $value) ? -$value : $value;
}
return $result;
}
}
Quick idea - go through the Roman number from right to left, if value of $current (more to the left) is smaller than $previous, then subtract it from the result, if larger, then add it.
$romanValues=array(
'I' => 1,
'V' => 5,
'X' => 10,
'L' => 50,
'C' => 100,
'D' => 500,
'M' => 1000,
);
$roman = 'MMMCMXCIX';
// RTL
$arabic = 0;
$prev = null;
for ( $n = strlen($roman) - 1; $n >= 0; --$n ) {
$curr = $roman[$n];
if ( is_null($prev) ) {
$arabic += $romanValues[$roman[$n]];
} else {
$arabic += $romanValues[$prev] > $romanValues[$curr] ? -$romanValues[$curr] : +$romanValues[$curr];
}
$prev = $curr;
}
echo $arabic, "\n";
// LTR
$arabic = 0;
$romanLength = strlen($roman);
for ( $n = 0; $n < $romanLength; ++$n ) {
if ( $n === $romanLength - 1 ) {
$arabic += $romanValues[$roman[$n]];
} else {
$arabic += $romanValues[$roman[$n]] < $romanValues[$roman[$n+1]] ? -$romanValues[$roman[$n]] : +$romanValues[$roman[$n]];
}
}
echo $arabic, "\n";
Some validation of roman number should also be added, though you said that you already have found how to do it.
Copyrights is for this blog (btw!)
http://scriptsense.blogspot.com/2010/03/php-function-number-to-roman-and-roman.html
<?php
function roman2number($roman){
$conv = array(
array("letter" => 'I', "number" => 1),
array("letter" => 'V', "number" => 5),
array("letter" => 'X', "number" => 10),
array("letter" => 'L', "number" => 50),
array("letter" => 'C', "number" => 100),
array("letter" => 'D', "number" => 500),
array("letter" => 'M', "number" => 1000),
array("letter" => 0, "number" => 0)
);
$arabic = 0;
$state = 0;
$sidx = 0;
$len = strlen($roman);
while ($len >= 0) {
$i = 0;
$sidx = $len;
while ($conv[$i]['number'] > 0) {
if (strtoupper(#$roman[$sidx]) == $conv[$i]['letter']) {
if ($state > $conv[$i]['number']) {
$arabic -= $conv[$i]['number'];
} else {
$arabic += $conv[$i]['number'];
$state = $conv[$i]['number'];
}
}
$i++;
}
$len--;
}
return($arabic);
}
function number2roman($num,$isUpper=true) {
$n = intval($num);
$res = '';
/*** roman_numerals array ***/
$roman_numerals = array(
'M' => 1000,
'CM' => 900,
'D' => 500,
'CD' => 400,
'C' => 100,
'XC' => 90,
'L' => 50,
'XL' => 40,
'X' => 10,
'IX' => 9,
'V' => 5,
'IV' => 4,
'I' => 1
);
foreach ($roman_numerals as $roman => $number)
{
/*** divide to get matches ***/
$matches = intval($n / $number);
/*** assign the roman char * $matches ***/
$res .= str_repeat($roman, $matches);
/*** substract from the number ***/
$n = $n % $number;
}
/*** return the res ***/
if($isUpper) return $res;
else return strtolower($res);
}
/* TEST */
echo $s=number2roman(1965,true);
echo "\n and bacK:\n";
echo roman2number($s);
?>
I'm late to the party, but here's mine. Assumes valid Numerals in the string, but doesn't test for a valid Roman number, whatever that is...there doesn't seem to be a consensus. This function will work for Roman numbers like VC (95), or MIM (1999), or MMMMMM (6000).
function roman2dec( $roman ) {
$numbers = array(
'I' => 1,
'V' => 5,
'X' => 10,
'L' => 50,
'C' => 100,
'D' => 500,
'M' => 1000,
);
$roman = strtoupper( $roman );
$length = strlen( $roman );
$counter = 0;
$dec = 0;
while ( $counter < $length ) {
if ( ( $counter + 1 < $length ) && ( $numbers[$roman[$counter]] < $numbers[$roman[$counter + 1]] ) ) {
$dec += $numbers[$roman[$counter + 1]] - $numbers[$roman[$counter]];
$counter += 2;
} else {
$dec += $numbers[$roman[$counter]];
$counter++;
}
}
return $dec;
}
Whew! Those are quite a few answers, and made of them are code-heavy! How about we define an algorithm for this first, before I give an answer?
The Basics
Don't store multi-digit Roman numerals, like 'CM' => 900, or anything like that in an array. If you know that M - C (1000 - 100) equals 900, then ultimately, you should only be storing the values of 1000 and 100. You wouldn't have multi-digit Roman numerals like CMI for 901, would you? Any answer that does this will be inefficient from one that understands the Roman syntax.
The Algorithm
Example: LIX (59)
Do a for loop on the numbers, starting at the end of the string of Roman numerals. In our example: We start on "X".
Greater-Than-Equal-To Case — If the value we are looking at is the same or greater than the last value, simply add it to a cumulative result. In our example: $result += $numeral_values["X"].
Less-Than Case — If the value we are subtracting is less than the previous number, we subtract it from our cumulative result. In our example IX, I is 1 and X is 10, so, since 1 is less than 10, we subtract it: giving us 9.
The Demo
Full Working Demo Online
The Code
function RomanNumeralValues() {
return [
'I'=>1,
'V'=>5,
'X'=>10,
'L'=>50,
'C'=>100,
'D'=>500,
'M'=>1000,
];
}
function ConvertRomanNumeralToArabic($input_roman){
$input_length = strlen($input_roman);
if($input_length === 0) {
return $result;
}
$roman_numerals = RomanNumeralValues();
$current_pointer = 1;
$result = 0;
for($i = $input_length - 1; $i > -1; $i--){
$letter = $input_roman[$i];
$letter_value = $roman_numerals[$letter];
if($letter_value === $current_pointer) {
$result += $letter_value;
} elseif ($letter_value < $current_pointer) {
$result -= $letter_value;
} else {
$result += $letter_value;
$current_pointer = $letter_value;
}
}
return $result;
}
print ConvertRomanNumeralToArabic("LIX");
function romanToInt($s) {
$array = ["I"=>1,"V"=>5,"X"=>10,"L"=>50,"C"=>100,"D"=>500,"M"=>1000];
$sum = 0;
for ($i = 0; $i < strlen($s); $i++){
$curr = $s[$i];
$next = $s[$i+1];
if ($array[$curr] < $array[$next]) {
$sum += $array[$next] - $array[$curr];
$i++;
} else {
$sum += $array[$curr];
}
}
return $sum;
}
Define your own schema! (optional)
function rom2arab($rom,$letters=array()){
if(empty($letters)){
$letters=array('M'=>1000,
'D'=>500,
'C'=>100,
'L'=>50,
'X'=>10,
'V'=>5,
'I'=>1);
}else{
arsort($letters);
}
$arab=0;
foreach($letters as $L=>$V){
while(strpos($rom,$L)!==false){
$l=$rom[0];
$rom=substr($rom,1);
$m=$l==$L?1:-1;
$arab += $letters[$l]*$m;
}
}
return $arab;
}
Inspired by andyb's answer
I just wrote this in about 10 mins, it's not perfect, but seems to work for the few test cases I've given it. I'm not enforcing what values are allowed to be subtracted from what, this is just a basic loop that compares the current letter value with the next one in the sequence (if it exists) and then either adds the value or adds the subtracted amount to the total:
$roman = strtolower($_GET['roman']);
$values = array(
'i' => 1,
'v' => 5,
'x' => 10,
'l' => 50,
'c' => 100,
'd' => 500,
'm' => 1000,
);
$total = 0;
for($i=0; $i<strlen($roman); $i++)
{
$v = $values[substr($roman, $i, 1)];
$v2 = ($i < strlen($roman))?$values[substr($roman, $i+1, 1)]:0;
if($v2 && $v < $v2)
{
$total += ($v2 - $v);
$i++;
}
else
$total += $v;
}
echo $total;
Just stumbled across this beauty and have to post it all over:
function roman($N)
{
$c = 'IVXLCDM';
for ($a = 5, $b = $s = ''; $N; $b++, $a ^= 7)
{
for (
$o = $N % $a, $N = $N / $a ^ 0;
$o--;
$s = $c[$o > 2 ? $b + $N - ($N &= -2) + $o = 1 : $b] . $s
);
}
return $s;
}
function Romannumeraltonumber($input_roman){
$di=array('I'=>1,
'V'=>5,
'X'=>10,
'L'=>50,
'C'=>100,
'D'=>500,
'M'=>1000);
$result=0;
if($input_roman=='') return $result;
//LTR
for($i=0;$i<strlen($input_roman);$i++){
$result=(($i+1)<strlen($input_roman) and
$di[$input_roman[$i]]<$di[$input_roman[$i+1]])?($result-$di[$input_roman[$i]])
:($result+$di[$input_roman[$i]]);
}
return $result;
}
function rom_to_arabic($number) {
$symbols = array(
'M' => 1000,
'D' => 500,
'C' => 100,
'L' => 50,
'X' => 10,
'V' => 5,
'I' => 1);
$a = str_split($number);
$i = 0;
$temp = 0;
$value = 0;
$q = count($a);
while($i < $q) {
$thys = $symbols[$a[$i]];
if(isset($a[$i +1])) {
$next = $symbols[$a[$i +1]];
} else {
$next = 0;
}
if($thys < $next) {
$value -= $thys;
} else {
$value += $thys;
}
$temp = $thys;
$i++;
}
return $value;
}
function parseRomanNumerals($input)
{
$roman_val = '';
$roman_length = strlen($input);
$result_roman = 0;
for ($x = 0; $x <= $roman_length; $x++) {
$roman_val_prev = $roman_val;
$roman_numeral = substr($input, $roman_length-$x,1);
switch ($roman_numeral) {
case "M":
$roman_val = 1000;
break;
case "D":
$roman_val = 500;
break;
case "C":
$roman_val = 100;
break;
case "L":
$roman_val = 50;
break;
case "X":
$roman_val = 10;
break;
case "V":
$roman_val = 5;
break;
case "I":
$roman_val = 1;
break;
default:
$roman_val = 0;
}
if ($roman_val_prev<$roman_val) {
$result_roman = $result_roman - $roman_val;
}
else {
$result_roman = $result_roman + $roman_val;
}
}
return abs($result_roman);
}
I have an array in the following format:
array(
0 => array(1, 5),
1 => array(4, 8),
2 => array(19, 24),
3 => array(6, 9),
4 => array(11, 17),
);
Where each item is a X-to-Y range. What I would like to merge the overlapping ranges in the array, to get something more like this:
array(
0 => array(1, 9), // 1-5, 4-8 and 6-9 are overlapping, so they are merged
1 => array(11, 17),
2 => array(19, 24),
);
What would be the best way to accomplish this?
Untested, but the idea here is to sort the data first by the first element, then merge subsequent elements with the previous one as long as possible.
usort($data, function($a, $b)
{
return $a[0] - $b[0];
});
$n = 0; $len = count($data);
for ($i = 1; $i < $len; ++$i)
{
if ($data[$i][0] > $data[$n][1] + 1)
$n = $i;
else
{
if ($data[$n][1] < $data[$i][1])
$data[$n][1] = $data[$i][1];
unset($data[$i]);
}
}
$data = array_values($data);
$input = array( 0 => array(1, 5),
1 => array(4, 8),
2 => array(19, 24),
3 => array(6, 9),
4 => array(11, 17),
);
$tmpArray = array();
foreach($input as $rangeSet) {
$tmpArray = array_unique(array_merge($tmpArray,range($rangeSet[0],$rangeSet[1])));
}
sort($tmpArray);
$oldElement = array_shift($tmpArray);
$newArray = array(array($oldElement));
$ni = 0;
foreach($tmpArray as $newElement) {
if ($newElement > $oldElement+1) {
$newArray[$ni++][] = $oldElement;
$newArray[$ni][] = $newElement;
}
$oldElement = $newElement;
}
$newArray[$ni++][] = $oldElement;
var_dump($newArray);
Alright, drafted this up, so it may have quirks. Tested it with the data seen below and seemed to work just fine. May not be the best way to do it, but it is one way and it does work. Questions let me know.
function combineRange($array) {
if (is_array($array)) {
// Sort the array for numerical order
sort($array);
// Set Defaults
$prev = array();
$prev_key = null;
foreach ($array as $key => $item) {
// First time around setup default data
if (empty($prev)) {
$prev = $item;
$prev_key = $key;
continue;
}
if ($item[0] >= $prev[0] && $item[0] <= $prev[1]) {
// Incase the last number was less than do not update
if ($array[$prev_key][1] < $item[1])
$array[$prev_key][1] = $item[1];
unset($array[$key]);
}else {
$prev_key = $key;
}
$prev = $item;
}
}
return $array;
}
$array = array(
5 => array(13, 16),
0 => array(1, 5),
1 => array(4, 8),
2 => array(19, 24),
3 => array(6, 9),
4 => array(11, 17),
6 => array(21, 30),
);
var_dump(combineRange($array));
Outputs:
array(3) {
[0]=>
array(2) {
[0]=>
int(1)
[1]=>
int(9)
}
[3]=>
array(2) {
[0]=>
int(11)
[1]=>
int(17)
}
[5]=>
array(2) {
[0]=>
int(19)
[1]=>
int(30)
}
}
Hope it works for ya!
EDIT
I see I was beaten out by an hour =\ Oh well! I am still posting as it is a different method, granted I would probably choose konforce's method instead.