Related
I have an $array and a $number, and I want to find the smallest combination (least number of elements) of the $array's elements, which sums to the $number, but I can't figure out how to do this with PHP code.
Test cases:
$array = [
'a' => '1',
'b' => '3',
'c' => '5',
'd' => '5',
'e' => '1',
'f' => '2',
'g' => '2',
];
If $number = 10, output should be 'c', 'd'
If $number = 1, output should be either 'a' or 'e'
If $number = 4, output should be either 'a', 'b' or 'b', 'e' or 'f', 'g'
If $number = 9 output should be 'a', 'b', 'c' or 'a', 'b', 'd' or 'c', 'f', 'g' etc.
How can I write this in code?
try this ( and use Recursive Algorithm )
<?php
$array= array("a"=>"1", "b"=>"3", "c"=>"5", "d"=>"5", "e"=>"1", "f"=>"2","g"=>"2");$num=6;
foreach ($array as $key => $value) {
if ($value==$num){
echo $key."=>".$value."<br>";
}
echo "-----------------"."<br>";
foreach( $array as $key1 => $value1) {
if($key1 > $key){
if(sumval($value,$value1)==$num){
echo $key."=>".$key1."<br>";
}
elseif(sumval($value,$value1)<$num){
$total=sumval($value,$value1);
foreach( $array as $key2 => $value2) {
if($key2 > $key1){
if(sumval($total,$value2)==$num){
echo $key."=>".$key1."=>".$key2."<br>";
}
}
}
}
}
}
}
function sumval($a,$b){
return $a+$b;
}
?>
You can try something like this
<?php
$array = ["a"=>"1", "b"=>"3", "c"=>"5", "d"=>"5", "e"=>"1", "f"=>"2", "g"=>"2"];
$new = $array;
$number = 19;//Change here to check all condition
$flag = false;
if(array_sum($new) == $number){
$temp = array_keys($new);
echo implode(",",$temp);
$flag = true;
exit;
}
if($flag)
exit;
foreach($array as $key=>$value){
if($value == $number){
echo $key."\n";
$flag = true;
exit;
}
}
if($flag)
exit;
foreach($array as $key=>$value){
$new = $array;
array_pop($new);
foreach($new as $key2=>$value2){
if($key!=$key2){
if(($value + $value2) == $number){
echo "$key , $key2 \n";
$flag = true;
exit;
}
}
}
}
if($flag)
exit;
$new = $array;
foreach($array as $key1=>$value1){
foreach($array as $key=>$value){
$new = $array;
unset($new[$key]);
if(array_sum($new) == $number){
$temp = array_keys($new);
echo implode(",",$temp);
exit;
}
array_pop($new);
}
array_pop($array);
}
?>
Live demo : https://eval.in/897632
Try this?
<?php
$array= array("a"=>"1", "b"=>"3", "c"=>"5", "d"=>"5", "e"=>"1", "f"=>"2", "g"=>"2");
$num = 4;
foreach ($array as $key => $value) {
$n1 = (int) $value;
if ($n1 === $num) {
echo $key.'<br/>';
break;
}
if ($n1 < $num) {
$n2 = $num - $n1;
if ($n2Key = isN2Exists("$n2", $array)) { // this probably can also be done using some PHP built-ins, but I just couldnt find the right one
echo "$key,$n2Key <br/>";
}
}
}
function isN2Exists($value, $array) {
foreach ($array as $k => $v) {
if ($v === $value) {
return $k;
}
}
return false;
}
This wont work if you need more than 2 numbers. For example, if the number is 9, you can not produce 9 with two numbers in your array. You need a,b,c or c,f,g. If you want it that way I think best way to do is to use recursion.
Update
If you want to do it through recursion, please try the following code. This should give you the exact output you are looking for:
<?php
$array= array("a"=>"1", "b"=>"3", "c"=>"5", "d"=>"5", "e"=>"1", "f"=>"2", "g"=>"2");
$num = 9;
function getLeastNumbers($num, $array, $sum = 0, $level = '', $resultSet = [], $combination = []) {
if ($sum > $num) {
return false;
}
foreach ($array as $key => $value) {
$n1 = (int) $value;
if (($n1 + $sum) == $num) {
$newCombination = $combination;
$newCombination[] = $key;
sort($newCombination);
$resultSet[] = $newCombination;
}
$combinationToSend = $combination;
$combinationToSend[] = $key;
sort($combinationToSend);
$array2 = $array;
unset($array2[$key]);
if ($return = getLeastNumbers($num, $array2, $n1 + $sum, "$level .", $resultSet, $combinationToSend)) {
$resultSet = array_unique(array_merge($resultSet, $return), SORT_REGULAR);
}
}
return $resultSet;
}
$list = getLeastNumbers($num, $array);
foreach($list as $item) {
$length = count($item);
$finalArray[$length][] = $item;
}
print_r($finalArray[min(array_keys($finalArray))]);
I have an array lets suppose
$myarr = array(
'1',
'2',
'3',
'4-7',
'9',
'10',
)
$search2 = '2';
$search5 = '5';
I want to check both these 2 variables inside my $myarr. The first one can be easily found by using in_array but the $search5 is there inside '4-7' but what will I do to find the value? I don't want 2 foreach because i know I can use explode and then compare the start and end value. Is there a single or double line function that could help me achieve what I need? Thanks
PHP code demo
<?php
ini_set("display_errors", 1);
$search=2;
$result=null;
$myarr = array(
'1',
'2',
'3',
'4-7',
'9',
'10',
);
echo search_in_array($myarr,$search);
function search_in_array($myarr,$search)
{
$result=false;
array_map(function($number) use ($myarr,$search, &$result){
if(preg_match("/^(\d+)\-(\d+)$/", $number,$matches))
{
if(in_array($search,range($matches[1],$matches[2])))
{
$result= true;
}
}
elseif(preg_match("/^(\d+)$/", $number,$matches))
{
if(in_array($search,$myarr))
{
$result= true;
}
}
}, $myarr);
return $result;
}
Just as another answer:
$myarr = [
'1',
'2',
'3',
'4-7',
'9',
'10',
];
$search2 = '2';
$search5 = '5';
$search12 = '12';
function myInArray($needle, $haystack) {
return in_array(
$needle,
array_reduce(
$haystack,
function($incrementor, $value) {
return array_merge($incrementor, (strpos($value, '-') !== false) ? range(...explode('-', $value)) : [(int)$value]);
},
[]
)
);
}
foreach([$search2, $search5, $search12] as $searchValue) {
echo $searchValue, " is ",(myInArray($searchValue, $myarr) ? '' : 'NOT '), "in the array", PHP_EOL;
}
Probably not as efficient, especially when working with larger ranges of values in $myarr, but a slightly different approach
Demo
Here is almost one-liner,
$search = '5';
$matches = explode('-',array_values(preg_grep('/[-]/', $myarr))[0]);
if(in_array($search, $myarr)|| in_array($search,range($matches[0],$matches[1]))){
echo "found";
}else{
echo "not found";
}
I am exploding by - and then creating range with that and checking that with in_array.
Here is working DEMO
<?php
$myarr = ['1', '2', '3', '4-7', '9', '10'];
$search_array = [2, 5];
$search_results = [];
for ($i = 0; $i < count($search_array); $i++) {
$search_results[$search_array[$i]] = array_search($search_array[$i], $myarr);
if (!$search_results[$search_array[$i]]) {
foreach ($myarr as $value) {
if (strpos($value, "-") !== false) {
$data = explode("-", $value);
if ($search_array[$i] >= $data[0] && $search_array[$i] <= $data[1]) {
$search_results[$search_array[$i]] = array_search($value, $myarr);
}
}
}
}
if (!$search_results[$search_array[$i]]) {
unset($search_results[$search_array[$i]]);
}
}
var_dump($search_results);
Obviously using single foreach i cam up with this code to find ... set of data another is for loop not foreach.
#Ali Zia you can also try this logic like below:
<?php
$myarr = array(
'1',
'2',
'3',
'4-7',
'9',
'10',
);
$flag = 0;
$search = '5';
foreach($myarr as $value){
if($value == $search){
$flag = 1;
break;
}
else if(strpos($value, "-")){
$numberRange = explode("-", $value);
if($numberRange[0] <= $search && $numberRange[1] >= $search){
$flag = 1;
break;
}
}
}
if($flag == 1){
echo "yes number found in the array";
}
else{
echo "sorry not found";
}
Just for the sake of using range() and argument unpacking, because none of the other answers do:
function in_array_range($needle, array $haystack) {
if (in_array((int) $needle, $haystack, true)) {
return true;
}
$haystack = array_map(
function($element) {
return strpos($element, '-')
? range(... explode('-', $element, 2))
: (int) $element;
},
$haystack
);
foreach ($haystack as $element) {
if (is_array($element) && in_array((int) $needle, $element, true)) {
return true;
}
}
return false;
}
The function below returns the index of $needle in $haystack or -1 if no such element has been found.
function find(array $haystack, $needle) {
for ($i = 0; $i < count($haystack); $i++) {
$parts = explode("-", $haystack[$i]);
if (count($parts) == 2) {
if ($needle >= $parts[0] && $needle <= $parts[1]) {
return $i;
}
}
else if ($needle == $parts[0]) {
return $i;
}
}
return -1;
}
The function assumes that $array only contains a single non-negative integer or two non-negative integers separated by a dash.
You can now collect the indices like this:
$indices = array();
foreach ($searchValues as $searchValue) {
$indices[] = find($myarr, $searchValue);
}
Try with following code :
$myarr = array('1','2','3','4-7','9','10');
$search = 1; // Try with 5
$flag1 = false;
foreach($myarr as $number){
if(!is_numeric($number)){
list($start,$end) = explode("-",$number);
if($search >=$start && $search <=$end ){
echo $search .' Found';
}
}else if($flag1 == false){
if(in_array($search,$myarr)){
echo $search .' Found';
$flag1 = true;
}
}
}
Working Demo
I know the answer has already been accepted but here is my approach w/ and w/o using foreach.
(See demo).
With foreach :
<?php
$value = 3;
foreach($myarr as $myval){
list($start, $end) = strpos($myval, '-') ? explode('-', $myval) : [$myval, $myval];
if($value >= $start && $value <= $end){
echo $value . ' found between "' . $start . '" & "' . $end . '" !' . "\n";
}
}
Without foreach, "one line" code :
<?php
$value = 5;
$trueValues = [1,5];
$falseValues = [5, 7, 8];
var_dump(!count(array_diff($trueValues,array_reduce($myarr,function($arr, $item){return $arr + (strpos($item, '-') ? range(...explode('-', $item)) : [$item]);},[]))));
var_dump(!count(array_diff($falseValues,array_reduce($myarr,function($arr, $item){return $arr + (strpos($item, '-') ? range(...explode('-', $item)) : [$item]);},[]))));
var_dump(in_array($value, array_reduce($myarr, function($arr, $item){return $arr + (strpos($item, '-') ? range(...explode('-', $item)) : [$item]);}, array())));
Unfolded :
<?php
var_dump(
!count(
array_diff(
$trueValues,
array_reduce(
$myarr,
function($arr, $item){
return $arr + (strpos($item, '-') ? range(...explode('-', $item)) : [$item]);
},
[]
)
)
)
);
EDIT : Didn't know the ellipsis operator, thank you #Mark Baker
This question already has answers here:
PHP - Check if two arrays are equal
(19 answers)
Closed last year.
The two arrays are considered equal since they have the same 1st dimension indexes (Electric, Gas, Water) the same 2nd dimension indexes (Gym, Library), and the same values for each intersect. The second level values will always be scalar (not arrays or objects). Order doesn't matter.
How can PHP verify that they are equal based on the above definition of equality?
$array1 = [
'Electric' => ['Gym' => 24, 'Library' => 25],
'Gas' => ['Gym' => 13, 'Library' => null],
'Water' => ['Gym' => null, 'Library' => null]
];
$array2 = [
'Gas' => ['Library' => null, 'Gym' => 13],
'Electric' => ['Gym' => 24, 'Library' => 25],
'Water' => ['Library' => null, 'Gym' => null]
];
My attempt is as follows...
if (count($arr1) != count($arr2) || array_diff($arr1, $arr2) !== array_diff($arr2, $arr1)) {
$error = 'Values do not match.';
}
Here's a recursive comparison function
function CompareRecursive($array1, $array2, &$mismatches) {
foreach ($array1 as $key => $value) {
if (!isset($array2[$key])) {
$mismatches[$key] = [ $value ];
continue;
}
$value2 = $array2[$key];
if (!is_array($value) || !is_array($value2)) {
if ($value != $value2) {
$mismatches[$key] = [
$value, $value2
];
}
} else {
$mismatches_internal = [];
CompareRecursive($value, $value2, $mismatches_internal);
if (!empty($mismatches_internal)) {
$mismatches[$key] = $mismatches_internal;
}
}
}
return empty($mismatches);
}
As an added bonus this keeps track of mismatched entries too, there's a drawback when using this method that it won't work if $array2 has extra elements that $array1 doesn't but you can resolve this by doing:
$isEqual = CompareRecursive($array1,$array2) && CompareRecursive($array2,$array1);
Simple code is:
$arr2;
$arr1;
$eq = true;
if (count($arr1) == count($arr2)) {
foreach ($arr1 as $k => $v) {
if (empty($arr2[$k]) || $arr2[$k]['Gym'] != $v['Gym'] || $arr2[$k]['Library'] != $v['Library']) {
$eq = false;
break;
}
}
} else {
$eq = false;
}
var_dump($eq);
Update:
Without fixed keys inner foreach becomes:
foreach ($arr1 as $k => $v) {
if (empty($arr2[$k]) || array_diff_assoc($arr2[$k], $v)) {
$eq = false;
break;
}
}
function arrayTwoLevelEquivalence(array $a, array $b) {
if (count($a) !== count($b)) {
return false;
}
foreach ($a as $k => $a2) {
if (!isset($b[$k])) {
return false;
}
$b2 = $b[$k];
if (count($a2) !== count($b2)) {
return false;
}
foreach ($a2 as $k2 => $v) {
if (!array_key_exists($k2, $b2) || $b2[$k2] !== $v) {
return false;
}
}
}
return true;
}
I decline to use array_diff_assoc() because I do not support unnecessary transient allocation of heap memory for the sake of convenience.
$array1 = array("a" => "green", 'b'=>"red", 'c'=>"blue");
$array2 = array('b'=>"red", 'c'=>"blue", "a" => "green");
echo empty(array_diff_assoc($array1, $array2));
check it, hope it will fulfill your requirements. It will check if both associative array are same, whether it has same order or not.
Check it for your multidimensional array -
function array_diff_assoc_recursive($array1, $array2)
{
foreach($array1 as $key => $value)
{
if(is_array($value))
{
if(!isset($array2[$key]))
{
$difference[$key] = $value;
}
elseif(!is_array($array2[$key]))
{
$difference[$key] = $value;
}
else
{
$new_diff = array_diff_assoc_recursive($value, $array2[$key]);
if($new_diff != FALSE)
{
$difference[$key] = $new_diff;
}
}
}
elseif(!isset($array2[$key]) || $array2[$key] != $value)
{
$difference[$key] = $value;
}
}
return !isset($difference) ? 0 : $difference;
}
I have found it from here
If all you need to know is they match or not - without any further details:
function ksortRecursive(&$array)
{
if (is_array($array)) {
ksort($array);
foreach ($array as &$arr) {
ksortRecursive($arr);
}
}
}
ksortRecursive($arr1);
ksortRecursive($arr2);
if (serialize($arr1) == serialize($arr2)) {
echo "match!";
}
Discount array
$Discount=arrray(
0=>array('FromArea'=>0,'ToArea'=>0,'Master'=>0,'Slave'=>0),
1=>array('FromArea'=>0,'ToArea'=>10,'Master'=>5,'Slave'=>0),
2=>array('FromArea'=>5,'ToArea'=>0,'Master'=>0,'Slave'=>8),
3=>array('FromArea'=>0,'ToArea'=>0,'Master'=>1,'Slave'=>2),
4=>array('FromArea'=>0,'ToArea'=>1,'Master'=>7,'Slave'=>5),
...
)
I want get discount amount base on input parameter of function.
If !empty(parameter) then check it in array
Like this
function DiscountAmount($FromArea, $ToArea, $Master, $Slave){
foreach ($Discounts as $R) {
if (!empty($FromArea) && empty($ToArea) && empty($Master) && empty($Slave)) {
if ($R["FromArea"] == $FromArea)
return true;
} else if (!empty($FromArea) && !empty($ToArea) && empty($Master) && empty($Slave)) {
if ($R["FromArea"] == $FromArea && $R["ToArea"] == $ToArea)
return true;
} else if (!empty($FromArea) && !empty($ToArea) && !empty($Master) && empty($Slave)) {
if ($R["FromArea"] == $FromArea && $R["ToArea"] == $ToArea && $R["Master"] == $Master)
return true;
} else if (!empty($FromArea) && !empty($ToArea) && !empty($Master) && !empty($Slave)) {
if ($R["FromArea"] == $FromArea && $R["ToArea"] == $ToArea && $R["Master"] == $Master && $R["Slave"] == $Slave)
return true;
} else if (!empty($FromArea) && !empty($ToArea) && !empty($Master) && !empty($Slave)) {
if ($R["FromArea"] == $FromArea && $R["ToArea"] == $ToArea && $R["Master"] == $Master && $R["Slave"] == $Slave)
return true;
}
...
}
}
$amout=DiscountAmount(0, 0, 0, 0);
$amout=DiscountAmount(1, 0, 0, 0);
$amout=DiscountAmount(0, 1, 0, 0);
$amout=DiscountAmount(0, 0, 1, 0);
$amout=DiscountAmount(0, 0, 0, 1);
$amout=DiscountAmount(1, 1, 0, 0);
$amout=DiscountAmount(1, 0, 1, 0);
$amout=DiscountAmount(1, 0, 0, 1;
$amout=DiscountAmount(1, 1, 1, 0);
$amout=DiscountAmount(1, 1, 0, 1);
$amout=DiscountAmount(1, 1, 1, 0);
$amout=DiscountAmount(1, 1, 1, 1);
....
Online Demo
But with this way you must check very case. Is there a simpler solution to do it?
function DiscountAmount($FromArea, $ToArea, $Master, $Slave)
{
//This line's simply to save time: if all fields are empty, no need to check further
if(empty($FromArea) && empty($ToArea) && empty($Master) && empty($Slave))
return true;
foreach ($Discounts as $R)
if (equiv($FromArea, $R["FromArea"]) &&
equiv($ToArea, $R["ToArea" ]) &&
equiv($Master, $R["Master" ]) &&
equiv($Slave, $R["Slave" ]))
return true;
return false;
}
function equiv($Field,$OtherField)
{
if (empty($Field)) return true;
else return $Field == $OtherField;
}
This ought to be significantly less clunky.
function DiscountAmount($FromArea = null, $ToArea = null, $Master = null, $Slave = null) {
global $Discounts; //pull discount array into the function
$argsCount = count(func_get_args()); //the number of arguments filled in
$checkArray = array(
'FromArea' => $FromArea, 'ToArea' => $ToArea, 'Master' => $Master, 'Slave' => $Slave
);
while (count($checkArray) < $argsCount) {
array_pop($checkArray);
}
foreach ($Discounts as $R) {
if ($checkArray == $R) { //checks if all key/value pairs are equal
return true;
}
}
return false;
}
Try with a regular expression:
function DiscountAmount($FromArea = null, $ToArea = null, $Master = null, $Slave = null){
if(empty($FromArea) && empty($ToArea) && empty($Master) && empty($Slave)) return true;
global $Discounts;
$json = json_encode($Discounts);
$reg = "/\{";
if(!empty($FromArea)) $reg .= "(?:[^\}]*\"FromArea\":{$FromArea})";
if(!empty($ToArea)) $reg .= "(?:[^\}]*\"ToArea\":{$ToArea})";
if(!empty($Master)) $reg .= "(?:[^\}]*\"Master\":{$Master})";
if(!empty($Slave)) $reg .= "(?:[^\}]*\"Slave\":{$Slave})";
$reg .= "[^\}]*\}/";
return preg_match($reg, $json);
}
I think this will eliminate the redundancy:
function DiscountAmount($Parameters){
foreach ($Discounts as $R) {
foreach ($Parameters as $PK => $P) {
$CheckFlag = true;
if (!empty($P)) {
if ($R[$PK] != $P) {
$CheckFlag = false;
break;
}
}
}
if (!empty($CheckFlag)) { return true; }
}
return false;
}
Assumptions:
Based on the question's code and the accepted answer:
If any of the arrays has non-empty values that match the input values, the function must return true
If all input arrays are empty, function must return true
Test
http://ideone.com/otiUJw
Any one have the ultimate PHP function(s) to add/remove parameters from a query string?
It needs to handle all possible cases, Ive seen ones that handle some cases, but not all.
Some example cases:
http://mysite.com?param1=1¶m2=2
http://www.mysite.com/?param1[]=1¶m1[]=2
ftp://user:pass#mysite.com/files/uploads/?param1=1¶m2=2
/?param1=1¶m2=2
/page.html?param1=1
/dir/page.html?test=1&bla=2
/dir/page.html?param1=1#jump_to_bottom
It should ideally be something like:
function add_get_param($uri, $name, $value = null) { ... }
function remove_get_param($uri, $name) { ... }
Some example tests:
$var = add_get_param('http://mysite.com?param1=1¶m2=2', 'param3', 3);
// http://mysite.com?param1=1¶m2=2¶m3=3
and:
$var = add_get_param('/dir/page.html?param1=1¶m2=2#jump_to_bottom', 'param3');
// /dir/page.html?param1=1¶m2=2¶m3#jump_to_bottom
etc...
Alright, I wrote my own functions:
PHP: http://pastebin.org/170157
jQuery: http://pastebin.org/169981
try the built in http_build_query and parse_str functions, they use associative arrays in the same style as $_GET as intermediates, but seem to do what you want...
I don't know about the ultimate solution. But PHP has several very helpful native functions that making your own defined function about should be easy.
Check out: html_build_query(), parse_str(), and parse_url()
This is the sketch for the function you can start with:
function add_get_param($url, $param, $value)
{
$parts_url = parse_url($url);
parse_str($parts_url['query'], $parts_query);
$parts_query[$param] = $value;
return $parts_url['scheme'] . '://' . $parts_url['host'] . '/' . $parts_url['path'] . '?' . http_build_query($parts_query);
}
var_dump(add_get_param('http://mysite.com?param1=1¶m2=2', 'param3', 3));
UPD: since parse_str breaks the data (replaces dots with underscores) I don't think this sketch is useful.
Alright I wrote my own, based on zerkms's sketch
class URL {
public static function each_get($url, $each_callback = null, $last_callback = null) {
$url = parse_url($url);
$result = '';
if (isset($url['scheme'])) $result .= $url['scheme'].'://';
if (isset($url['user'])) {
$result .= $url['user'];
if (isset($url['pass'])) $result .= ':'.$url['pass'];
$result .= '#';
}
if (isset($url['host'])) $result .= $url['host'];
if (isset($url['path'])) $result .= $url['path'];
if (!isset($url['query'])) $url['query'] = '';
$query = array();
$callable = is_callable($each_callback);
foreach (explode('&', $url['query']) as $param) {
if ($param == '') {
continue;
}
if (!$callable) {
$query[] = $param;
continue;
}
$callback_result = $each_callback($param);
if ($callback_result === true) {
$query[] = $param;
} elseif ($callback_result !== false) {
$query[] = $callback_result;
}
}
if (is_callable($last_callback)) {
$query = $last_callback($query);
}
$query = implode('&', $query);
$result .= strlen($query) ? '?'.$query : '';
if (isset($url['fragment'])) $result .= '#'.$url['fragment'];
return $result;
}
public static function add_get($url, $new_param, $new_value = null) {
return
static::each_get($url, function($param) {
$param = explode('=', $param);
if (isset($param[1])) {
return $param[0].'='.$param[1];
}
return $param[0];
}, function($query) use($new_param, $new_value) {
if ($new_value === null) {
$query[] = $new_param;
} else {
$query[] = $new_param.'='.$new_value;
}
return $query;
});
}
public static function remove_get($url, $remove_param) {
return
static::each_get($url, function($param) use($remove_param) {
$param = explode('=', $param);
return $param[0] != $remove_param;
});
}
public static function replace_get($url, $name, $value = null) {
return static::add_get(static::remove_get($url, $name), $name, $value);
}
}
And here is my limited test cases:
function test($test, $result) {
static $i;
$i++;
if ($test !== $result) {
echo $i.' Fail: got '.$test.' should be '.PHP_EOL.' '.$result.'<br>'.PHP_EOL;
} else {
echo $i.' Pass: '.$test.'<br>'.PHP_EOL;
}
}
$urls = array(
0 => 'http://user:pass#www.site.com?a=1',
1 => 'http://www.site.com?a=1',
2 => '/dir/page.php?a=1',
3 => '/dir/?a=1',
4 => '/dir?a=1',
5 => '/?a=1',
6 => '?a=1',
7 => 'http://user:pass#www.site.com?a=1#hash',
8 => 'http://www.site.com?a=1#hash',
9 => '/dir/page.php?a=1#hash',
10 => '/dir/?a=1#hash',
11 => '/dir?a=1#hash',
12 => '/?a=1#hash',
13 => '?a=1#hash',
14 => 'http://www.site.com/?a=1',
15 => 'http://www.site.com/?a=1#hash',
16 => '/dir/page.php?a=1&b=2&c=3',
);
test(URL::add_get($urls[0], 'b', 2), 'http://user:pass#www.site.com?a=1&b=2');
test(URL::add_get($urls[1], 'b', 2), 'http://www.site.com?a=1&b=2');
test(URL::add_get($urls[2], 'b', 2), '/dir/page.php?a=1&b=2');
test(URL::add_get($urls[3], 'b', 2), '/dir/?a=1&b=2');
test(URL::add_get($urls[4], 'b', 2), '/dir?a=1&b=2');
test(URL::add_get($urls[5], 'b', 2), '/?a=1&b=2');
test(URL::add_get($urls[6], 'b', 2), '?a=1&b=2');
test(URL::add_get($urls[7], 'b', 2), 'http://user:pass#www.site.com?a=1&b=2#hash');
test(URL::add_get($urls[8], 'b', 2), 'http://www.site.com?a=1&b=2#hash');
test(URL::add_get($urls[9], 'b', 2), '/dir/page.php?a=1&b=2#hash');
test(URL::add_get($urls[10], 'b'), '/dir/?a=1&b#hash');
test(URL::add_get($urls[11], 'berLongBla 1235_+'), '/dir?a=1&berLongBla 1235_+#hash');
test(URL::add_get($urls[12], 'a', 2), '/?a=1&a=2#hash');
test(URL::add_get($urls[13], 'a[]', 2), '?a=1&a[]=2#hash');
test(URL::add_get($urls[14], 'b', 2), 'http://www.site.com/?a=1&b=2');
test(URL::add_get($urls[15], 'b', 2), 'http://www.site.com/?a=1&b=2#hash');
test(URL::remove_get($urls[0], 'a'), 'http://user:pass#www.site.com');
test(URL::remove_get($urls[1], 'a'), 'http://www.site.com');
test(URL::remove_get($urls[2], 'a'), '/dir/page.php');
test(URL::remove_get($urls[3], 'a'), '/dir/');
test(URL::remove_get($urls[4], 'a'), '/dir');
test(URL::remove_get($urls[5], 'a'), '/');
test(URL::remove_get($urls[6], 'a'), '');
test(URL::remove_get($urls[16], 'b'), '/dir/page.php?a=1&c=3');
I also converted it to JavaScript/jQuery
URL = {};
URL.parse_url = function(str, component) {
var o = {
strictMode: false,
key: ["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"],
q: {
name: "queryKey",
parser: /(?:^|&)([^&=]*)=?([^&]*)/g
},
parser: {
strict: /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:#]*):?([^:#]*))?#)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/,
loose: /^(?:(?![^:#]+:[^:#\/]*#)([^:\/?#.]+):)?(?:\/\/\/?)?((?:(([^:#]*):?([^:#]*))?#)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/ // Added one optional slash to post-protocol to catch file:/// (should restrict this)
}
};
var m = o.parser[o.strictMode ? "strict" : "loose"].exec(str),
uri = {},
i = 14;
while (i--) {uri[o.key[i]] = m[i] || "";}
switch (component) {
case 'PHP_URL_SCHEME':
return uri.protocol;
case 'PHP_URL_HOST':
return uri.host;
case 'PHP_URL_PORT':
return uri.port;
case 'PHP_URL_USER':
return uri.user;
case 'PHP_URL_PASS':
return uri.password;
case 'PHP_URL_PATH':
return uri.path;
case 'PHP_URL_QUERY':
return uri.query;
case 'PHP_URL_FRAGMENT':
return uri.anchor;
default:
var retArr = {};
if (uri.protocol !== '') {retArr.scheme=uri.protocol;}
if (uri.host !== '') {retArr.host=uri.host;}
if (uri.port !== '') {retArr.port=uri.port;}
if (uri.user !== '') {retArr.user=uri.user;}
if (uri.password !== '') {retArr.pass=uri.password;}
if (uri.path !== '') {retArr.path=uri.path;}
if (uri.query !== '') {retArr.query=uri.query;}
if (uri.anchor !== '') {retArr.fragment=uri.anchor;}
return retArr;
}
}
URL.each_get = function(url, each_callback, last_callback) {
url = URL.parse_url(url);
var result = '';
if (url.scheme) result += url.scheme+'://';
if (url.user) {
result += url.user;
if (url.pass) result += ':'+url.pass;
result += '#';
}
if (url.host) result += url.host;
if (url.path) result += url.path;
if (!url.query) url.query = '';
var query = [];
$.each(url.query.split('&'), function(key, param) {
if (param == '') {
return;
}
if (!each_callback) {
query.push(param);
return;
}
var callback_result = each_callback(param);
if (callback_result === true) {
query.push(param);
} else if (callback_result !== false) {
query.push(callback_result);
}
});
if (last_callback) {
query = last_callback(query);
}
query = query.join('&');
result += query.length ? '?'+query : '';
if (url.fragment) result += '#'+url.fragment;
return result;
}
URL.add_get = function(url, new_param, new_value) {
return URL.each_get(url, function(param) {
param = param.split('=');
if (typeof param[1] != 'undefined') {
return param[0]+'='+param[1];
}
return param[0];
}, function(query) {
if (typeof new_value == 'undefined') {
query.push(new_param);
} else {
query.push(new_param+'='+new_value);
}
return query;
});
}
URL.remove_get = function(url, remove_param) {
return URL.each_get(url, function(param) {
param = param.split('=');
return param[0] != remove_param;
});
}
URL.replace_get = function(url, name, value) {
return URL.add_get(URL.remove_get(url, name), name, value);
}
var i = 0;
function test(test, result) {
i++;
if (test !== result) {
console.debug(i+' Fail: got '+test+' should be '+result);
} else {
console.debug(i+' Pass: '+test);
}
}
And the limited text cases:
var urls = {
0 : 'http://user:pass#www.site.com?a=1',
1 : 'http://www.site.com?a=1',
2 : '/dir/page.php?a=1',
3 : '/dir/?a=1',
4 : '/dir?a=1',
5 : '/?a=1',
6 : '?a=1',
7 : 'http://user:pass#www.site.com?a=1#hash',
8 : 'http://www.site.com?a=1#hash',
9 : '/dir/page.php?a=1#hash',
10 : '/dir/?a=1#hash',
11 : '/dir?a=1#hash',
12 : '/?a=1#hash',
13 : '?a=1#hash',
14 : 'http://www.site.com/?a=1',
15 : 'http://www.site.com/?a=1#hash',
16 : '/dir/page.php?a=1&b=2&c=3'
};
test(URL.add_get(urls[0], 'b', 2), 'http://user:pass#www.site.com?a=1&b=2');
test(URL.add_get(urls[1], 'b', 2), 'http://www.site.com?a=1&b=2');
test(URL.add_get(urls[2], 'b', 2), '/dir/page.php?a=1&b=2');
test(URL.add_get(urls[3], 'b', 2), '/dir/?a=1&b=2');
test(URL.add_get(urls[4], 'b', 2), '/dir?a=1&b=2');
test(URL.add_get(urls[5], 'b', 2), '/?a=1&b=2');
test(URL.add_get(urls[6], 'b', 2), '?a=1&b=2');
test(URL.add_get(urls[7], 'b', 2), 'http://user:pass#www.site.com?a=1&b=2#hash');
test(URL.add_get(urls[8], 'b', 2), 'http://www.site.com?a=1&b=2#hash');
test(URL.add_get(urls[9], 'b', 2), '/dir/page.php?a=1&b=2#hash');
test(URL.add_get(urls[10], 'b'), '/dir/?a=1&b#hash');
test(URL.add_get(urls[11], 'berLongBla 1235_+'), '/dir?a=1&berLongBla 1235_+#hash');
test(URL.add_get(urls[12], 'a', 2), '/?a=1&a=2#hash');
test(URL.add_get(urls[13], 'a[]', 2), '?a=1&a[]=2#hash');
test(URL.add_get(urls[14], 'b', 2), 'http://www.site.com/?a=1&b=2');
test(URL.add_get(urls[15], 'b', 2), 'http://www.site.com/?a=1&b=2#hash');