Sort array multidimensional by first subarray - php

I have an array and I want to sort it:
$ArrayStock = array(array("SIZE","L","XL","M"),("STOCK","12","3","2"));
How can I sort the array by "SIZE"?
Alternatively, I could use this array as a starting point:
$ArrayStock = array(array("L","XL","M"),("12","3","2"));

You could do it like this:
// Combine into one associative array
$combined = array_combine($ArrayStock[0], $ArrayStock[1]);
// Rebuild it in the correct order:
foreach(["SIZE", "S","M","L","XL","XXL"] as $size) {
if (isset($combined[$size])) $result[$size] = $combined[$size];
}
// Split associative array back to its original structure:
$ArrayStock = [array_keys($result), array_values($result)];
Note that this structure is not so practical in its use. I would in fact stick with the associative array.

There will be a few ways to skin this cat. All of my methods will produce the expected output...
First, let's do it without restructuring your array (Demo):
$order=["XS","S","M","L","XL","2XL","3XL"]; // declare appropriate size order
$ArrayStock=[["L","XL","M"],["12","3","2"]];
// Order the first subarray:
uasort($ArrayStock[0],function($a,$b)use($order){
return (array_search($a,$order)>array_search($b,$order)?1:-1); // any unidentifed sizes will go to the front
});
// Sync the second subarray with first subarray:
$ArrayStock[1]=array_replace($ArrayStock[0],$ArrayStock[1]);
// Optionally re-index the keys:
$ArrayStock[0]=array_values($ArrayStock[0]);
$ArrayStock[1]=array_values($ArrayStock[1]);
Next I'll show a few ways that you can manipulate a restructured array. There is absolutely nothing wrong with the way that trincot has written his. These are just alternatives that I've come up with...
I agree with trincot about using sizes as keys (because they will be unique) and stock counts as values. So the first process is to generate that new array structure:
$ArrayStock=[["L","XL","M"],["12","3","2"]];
#1 One-liner array_combine() approach:
$new_structure=array_combine($ArrayStock[0],$ArrayStock[1]);
// $new_structure = ['L'=>'12','XL'=>'3','M'=>'2']
#2 foreach() approach:
foreach($ArrayStock[0] as $i=>$size){
$new_structure[$size]=$ArrayStock[1][$i];
}
// $new_structure = ['L'=>'12','XL'=>'3','M'=>'2']
Now to sort the new array, you can use uksort() or a range of other array functions / loops with your predetermined order array:
$order=["XS","S","M","L","XL","2XL","3XL"]; // declare whatever appropriate sizes in order
#1 uksort() with array_search() approach:
uksort($new_structure,function($a,$b)use($order){
return (array_search($a,$order)>array_search($b,$order)?1:-1);
// any unidentified sizes will go to the front of the array
});
// keep in mind, this doesn't declare $result, it sorts $new_structure
#2 array_replace() with array_flip()-array_intersect()-array_keys() approach:
$result=array_replace( // replace values on matching keys
array_flip( // swap keys with values
array_intersect(
$order, // keep values from here
array_keys($new_structure) // that exist here
)
),
$new_structure); // use these elements for replace
// $result=['M'=>'2','L'=>'12','XL'=>'3'];
#3 foreach() with array_intersect()-array_keys() approach:
// generate & iterate ordered array
foreach(array_intersect($order,array_keys($new_structure)) as $v){
$result[$v]=$new_structure[$v]; // build properly sorted array
}
// $result=['M'=>'2','L'=>'12','XL'=>'3'];
Finally as trincot has shown, you can revert the sorted data back to the initial format with one more one-liner:
$ArrayStock=[array_keys($result),array_values($result)];

Related

Store Get-Values from form in Array PHP

I'm trying to store the users's input via the method get in an array to store it and further process it without overwriting the initial get-value. But I dont know how.. do I have to store them in a database to do that? Or can I just push every input into an array?
I believe the following should work for you... This will take all the $_GETs that you supply and put them in a new array so you can modify them without affecting the original $_GET array.
if(is_array($_GET)){
$newArr = $_GET; // modify $newArr['postFieldName'] instead of $_GET['postFieldName'] to preserve original $_GET but have new array.
}
That solution there will dupe the $_GET array. $_GET is just an internal PHP array of data, as is $_POST. You could also loop through the GETs if you do not need ALL of the GETs in your new array... You would do this by setting up an accepted array of GETs so you only pull the ones you need (this should be done anyways, as randomly accepting GETs from a form can lead to some trouble if you are also using the GETs for database/sql functions or anything permission based).
if(is_array($_GET) && count($_GET) > 0){
$array = array();
$theseOnly = array("postName", "postName2");
foreach($_GET as $key => $value){
if(!isset($array[$key]) && in_array($key, $theseOnly)){ // only add to new array if they are in our $theseOnly array.
$array[$key] = $value;
}
}
print_r($array);
} else {
echo "No $_GET found.";
}
I would just add to what #Nerdi.org said.
Specifically the second part, instead of looping through the array you can use either array_intersect_key or array_diff_key
$theseOnly = array("postName", "postName2");
$get = array_intersect_key( $_GET, array_flip($theseOnly);
//Or
$get = array_diff_key( $_GET, array_flip($theseOnly);
array_intersect_key
array_intersect_key() returns an array containing all the entries of array1 which have keys that are present in all the arguments.
So this one returns only elements you put in $theseOnly
array_diff_key
Compares the keys from array1 against the keys from array2 and returns the difference. This function is like array_diff() except the comparison is done on the keys instead of the values.
So this one returns the opposite or only elements you don't put in $theseOnly
And
array_flip
array_flip() returns an array in flip order, i.e. keys from array become values and values from array become keys.
This just takes the array of names with no keys (it has numeric keys by default), and swaps the key and the value, so
$theseOnly = array_flip(array("postName", "postName2"));
//becomes
$theseOnly = array("postName"=>0, "postName2"=>1);
We need the keys this way so they match what's in the $_GET array. We could always write the array that way, but if your lazy like me then you can just flip it.
session_start();
if(!isset($_SESSION['TestArray'])) $_SESSION['TestArray'] = array();
if(is_array($_GET)){
$_SESSION['TestArray'][] = $_GET;
}
print_r($_SESSION['TestArray']);
Thanks everybody for helping! This worked for me!

Remove element from associative array in PHP

I need to remove a element from an associative array with an string structure.
Example:
$array = array(
"one"=>array("Hello", "world"),
"two"=>"Hi"
)
I want to create a function that removes the elements like this:
function removeElement($p) {
// With the information provided in $p something like this should happen
// unset($array["one"]["hello"])
}
removeElement("one.hello");
Your base array is associative, the inner array (key one) is not, its a indexed array, which you can not access via ["hello"] but rather [0].
You can remove the hello value by using the unset function, but the indexes will stay as they are:
$array = ['Hello', 'World']; // array(0: Hello, 1: World)
unset($array[0]); // Array is now array(1: World)
If you wish to keep unset and keep the array indexes in order, you can fetch the values using the array_values function after unset:
unset($array[0]);
$array = array_values($array); // array(0: World)
Or you could use array_splice.
When it comes to using a string as key for multidimensional array with a dot-separator I'd recommend taking a look at laravels Arr::forget method which does pretty much exactly what you are asking about.
This would be a static solution to your question, in any case, you need to use explode.
function removeElement($p, $array) {
$_p = explode('.', $p);
return unset($array[$_p[0]][$_p[1]]);
}
But bear in mind, this doesn't work if you have more in $p (like: foo.bar.quux)

php merge elements in an array of arrays with common and different keys into a final array

Suppose I have single array of arrays. each element in the array of arrays are similar to each other as they have similar keys, but perhaps one of the keys has a unique value or one of the arrays in the array of arrays has an additional/different key. For example...
$masterArray = array(
[0]=>array(id=>'123', url=>"http://xyz.com", data=>"something", data2=>"else"),
[1]=>array(id=>'123', url=>"http://xyz.com", data=>"something", data3=>"baby"),
[2]=>array(id=>'456', url=>"http://abc.com", data=>"something", data2=>"completely"),
[3]=>array(id=>'456', url=>"http://abc.com", data=>"something", data3=>"different"),
[4]=>array(id=>'789', url=>"http://def.com", data=>"something", data2=>"is not quite"),
[5]=>array(id=>'789', url=>"http://def.com", data=>"something", data3=>"right")
);
I require a new array to be made from $masterArray that would merge the individual keys together in the array of arrays into a new key in the new array, based on matching key value pairs in each individual array.
So the final array would appear like this...
$finalArray = array(
[0]=>array(id=>'123', url=>"http://xyz.com", data=>"something", data2=>"else", data3=>"baby"),
[1]=>array(id=>'456', url=>"http://abc.com", data=>"something", data2=>"completely", data3=>"different"),
[2]=>array(id=>'789', url=>"http://def.com", data=>"something", data2=>"is not quite", data3=>"right"),
);
There's quite an array (no pun intended) of php functions that can sort / merge arrays or values, but I cannot figure out which ones to use or how to implement it!
Can anyone figure out a solution to this? It would be much appreciated.
Note: To note, I have done the following
What I have tried is the following algorithm:
create two copies of $masterArray (call them $m1 and $m2)
use two for..each loops. The first for...each loop goes through $m1, the second one through $m2
grab the current element in $m1 from the outer loop
compare the current element in $m2 from the inner loop
if the $m1[outerloopindex] matches $m2[innerloopindex] through the majority of their key values, create a new array element $x, merging their key value pairs.
push or add $x to $newArray
(and this may or may not work) remove the element compared from $m1 and $m2 (since we used them, we don't need them anymore. assume no more matches)
if the $m1[outerloopindex] does not match$m2[innerloopindex] , simply go to the next $m2[innerloopindex] value and compare again to $m1[outerloopindex] until a match is made (or not)
if no elements in $m2 match the element compared against in $m1, keep $m1[outerloopindex]
loop again going to $m1[outerloopindex+1] etc.
But unfortunately it does not seem to return the correct number of results :(
Sample code is below.
foreach($artistData as $key=>&$asset){
// 2. examine each element, its value is itself an array.
// $key returns the position
// reset the $artistData array
$artistData=array_values($artistData);
$currElement = $artistData[$key];
// 3. check for this secondary array's uid.
// loop through each element in this secondary array
$currElUid = $currElement['uid'];
$currElid = $currElement['id'];
$currElThumbUrl = $currElement['thumbUrl'];
// 4. then proceed down the remaining array and compare the remaining indice's array's uid with the current one being explored
$artistCount=0;
// reset the second array $artistData02;
$artistData02 = array_values($artistData02);
foreach($artistData02 as $key02=>&$asset02){
// clear our temporary new element
unset($resultElement);
$resultElement=array();
// grab the new element to compare to.
// make sure it's not the same as the original
$newCurrElement = $artistData02[$key02];
$newCurrElUid = $newCurrElement['uid'];
$newCurrElid = $newCurrElement['id'];
$newCurrElThumbUrl = $newCurrElement['thumbUrl'];
// if I already compared this element, then skip it entirely
// We also don't want to compare the element to itself.
if ($key!=$key02){
// make sure the uids match
if($currElUid==$newCurrElUid){
// make sure the thumb URLs are different
if($currElThumbUrl!=$newCurrElThumbUrl){
// create the new merged element
// grab the filetype of $currElement
switch($currElement['filetype']){
case 1:
$resultElement['imageLge']=$currElement['publicUrl'];
$resultElement['imageSml']=$currElement['thumbUrl'];
break;
case 3:
$resultElement['song']=$currElement['publicUrl'];
break;
}
// then we compare the newCurrElement and add our information
switch($newCurrElement['filetype']){
case 1:
$resultElement['imageLge']=$newCurrElement['publicUrl'];
$resultElement['imageSml']=$newCurrElement['thumbUrl'];
break;
case 3:
$resultElement['song']=$newCurrElement['publicUrl'];
break;
}
// for the remaining values, we will pass as is
$resultElement['title']=$currElement['title'];
$resultElement['uid']=$currElement['uid'];
// take the resultant $resultElement and merge it to the main array (before $resultELement is recreated)
array_push($resultArr,$resultElement);
// this will reflow the elements, and change the indexing(so the echo merge statement will change). but comparisons will be reduced
unset($artistData[$key]);
$artistData = array_values($artistData);
unset($artistData02[$key02]);
$artistData02 = array_values($artistData02);
}else{
//echo "The thumbURLs are the same. skipping... <br />";
}
}else{
//echo "not the same uids. skipping... <br />";
}
}else{
//echo "this is the same element. skipping... <br />";
}
$artistCount++;
// end inner for...each loop
}
// 7. loop and check through the remaining elements in main array again.
}
/* end outer for each loop */
// add another element - the artist count at the end
$resultArr['artistCount']= $artistCount-1;
// build a JSON object that contains the artists, the total number
echo $resultArr;
again, any help is appreciated...
To answer your original question, this code will intake your masterArray and output your finalArray:
// sample array just for completeness
$masterArray = array(
array('id'=>'123', 'url'=>"http://xyz.com", 'data'=>"something", 'data2'=>"else"),
array('id'=>'123', 'url'=>"http://xyz.com", 'data'=>"something", 'data3'=>"baby"),
array('id'=>'456', 'url'=>"http://abc.com", 'data'=>"something", 'data2'=>"completely"),
array('id'=>'456', 'url'=>"http://abc.com", 'data'=>"something", 'data3'=>"different"),
array('id'=>'789', 'url'=>"http://def.com", 'data'=>"something", 'data2'=>"is not quite"),
array('id'=>'789', 'url'=>"http://def.com", 'data'=>"something", 'data3'=>"right")
);
// here's where the code actually begins
$finalArray = array();
foreach( $masterArray as $m )
{
if( !isset( $finalArray[$m['id']] ) )
$finalArray[$m['id']] = $m;
else
$finalArray[$m['id']] = array_merge( $finalArray[$m['id']], $m );
}
$finalArray = array_values( $finalArray );
If you var_dump $finalArray, it'll line up with what you wanted to get. This is matching based on id.
Your sample code I haven't taken the time to read thoroughly, is aggregating values based on id enough? If so, answer is above. ;)

PHP json_decode array cannot retrieve numerical index?

I have an array that is associative that I have decoded from a json json_decode second value true and looks like
Array (
[test] => Array
(
[start] => 1358766000
[end] => 1358775000
[start_day] => 21
[end_day] => 21
)
)
But for some reason when I do $array[0] I get null? How can I get the array by index? Not by key name?
array_values() will give you all the values in an array with keys renumbered from 0.
The first level of the array is not numerical, it's an associative array. You need to do:
$array['test']['start']
Alternatively, to get the first element:
reset($array);
$first_key = key($array);
print_r($array[$first_key]);
You could use current.
$first = current($array); // get the first element (in your case, 'test')
var_dump($first);
This is by design . . . your JSON used a key (apparently test), which contained a JSON object. The keys are preserved when you do a json_decode. You can't access by index, though you could loop through the whole thing using a foreach.
From your comment, it sounds like you want to access previous and next elements from an associative array. I don't know a way to do this directly, but a hackish way would be as follows:
$testArr = array('a'=>'10', 'b'=>'2', 'c'=>'4');
// get numeric index of the element of interest
$keys = array_keys($testArr);
$i = array_search('b', $keys);
// get key of next element
$nextElementKey = $keys[$i+1];
// next element value
$nextElementValue = $testArry[$nextElementKey];
// get key of previous element
$prevElementKey = $keys[$i-1];
// prev value
$[prevElementValue = $testArry[$prevElementKey];
You'd probably want to add some error checking around the previous and next key calculations to handle the first and last values.
If you don't care about the data in the key, Ignacio's solution using array_keys is much more efficient.

Autofill array with empty data to match another array size

I have 2 sets of arrays:
$dates1 = array('9/12','9/13','9/14','9/15','9/16','9/17');
$data1 = array('5','3','7','7','22','18');
// for this dataset, the value on 9/12 is 5
$dates2 = array('9/14','9/15');
$data2 = array('12','1');
As you can see the 2nd dataset has fewer dates, so I need to "autofill" the reset of the array to match the largest dataset.
$dates2 = array('9/12','9/13','9/14','9/15','9/16','9/17');
$data2 = array('','','12','1','','');
There will be more than 2 datasets, so I would have to find the largest dataset, and run a function for each smaller dataset to properly format it.
The function I'd create is the problem for me. Not even sure where to start at this point. Also, I can format the date and data arrays differently (multidimensional arrays?) if for some reason that is better.
You can do this in a pretty straightforward manner using some array functions. Try something like this:
//make an empty array matching your maximum-sized data set
$empty = array_fill_keys($dates1,'');
//for each array you wish to pad, do this:
//make key/value array
$new = array_combine($dates2,$data2);
//merge, overwriting empty keys with data values
$new = array_merge($empty,$new);
//if you want just the data values again
$data2 = array_values($new);
print_r($data2);
It would be pretty easy to turn that into a function or put it into a for loop to operate on your array sets. Turning them into associative arrays of key/value pairs would make them easier to work with too I would think.
If datas are related will be painful to scatter them on several array.
The best solution would be model an object with obvious property names
and use it with related accessor.
From your question I haven't a lot of hint of what data are and then I have to guess a bit:
I pretend you need to keep a daily log on access on a website with downloads. Instead of using dates/data1/data2 array I would model a data structure similar to this:
$log = array(
array('date'=>'2011-09-12','accessCount'=>7,'downloadCount'=>3),
array('date'=>'2011-09-13','accessCount'=>9), /* better downloadsCount=>0 though */
array('date'=>'2011-09-15','accessCount'=>7,'downloadCount'=>3)
...
)
Using this data structure I would model a dayCollection class with methods add,remove,get,set, search with all methods returning a day instance (yes, the remove too) and according signature. The day Class would have the standard getter/setter for every property (you can resolve to magic methods).
Depending on the amount of data you have to manipulate you can opt to maintain into the collection just the object data (serialize on store/unserialize on retrieve) or the whole object.
It is difficult to show you some code as the question is lacking of details on your data model.
If you still want to pad your array than this code would be a good start:
$temp = array($dates, $data1, $data2);
$max = max(array_map('count',$temp));
$result = array_map( function($x) use($max) {
return array_pad($x,$max,0);
}, $temp);
in $result you have your padded arrays. if you want to substitute your arrays do a simple
list($dates, $data1, $data2) = array_map(....
You should use hashmaps instead of arrays to associate each date to a data.
Then, find the largest one, cycle through its keys with a foreach, and test the existence of the same key in the small one.
If it doesn't exist, create it with an empty value.
EDIT with code (for completeness, other answers seem definitely better):
$dates_data1 = array('9/12'=>'5', '9/13'=>'3', '9/14'=>'7' /* continued */);
$dates_data2 = array('9/14'=>'12', '9/15'=>'1');
#cycle through each key (date) of the longest array
foreach($dates_data1 as $key => $value){
#check if the key exists in the smallest and add '' value if it does not
if(!isset( $date_data2[$key] )){ $date_data2[$key]=''; }
}

Categories