Jagged Edge Arrays in PHP - php

I want to store some data in (I guess a semi-2, semi-3d array) in PHP (5.3)
What I need to do is store data about each floor like this:
Floor Num of Spots Handicap Motorcyle Other
1 100 array(15,16,17) array (47,62) array (99,100)
2 100 array(15,16,17) array (47,62) array (99,100)
and on
The problem is, is if the Handicap+Motorcyle+Other were ints, I could just store the data in a 2d array. However, they aren't. So I was thinking I could make something almost like a 3D array, with the first two columns only being in 2D.
The other thought I had was making a 2D array and for columns 3,4, and 5 instead of saving as
array(15,16)
//save like
1516
And then split at two digits (1 digit array numbers would be prefaced with a 0). However, I am wondering about the limit of the length of a string, because if I decide to move to a 3 digit length number in the array, like array(100, 104), and I need to store alot of numbers, I am thinking I am going to quickly exceed the max.
Edit 1
I like Omar's answer alot, but I'm not sure as to how to pull the data out.

While you could store them as ?D array, there is another approach you might want to consider :
$stuff = array (
'floor1' =>
array (
'NumSpots' => 100,
'handicap' => array (15,16,17),
'motorcycle' => array (47, 62),
'other' => array (99, 100),
),
),
'floor2' =>
'NumSpots' => 100,
'handicap' => array (15,16,17),
'motorcycle' => array (47, 62),
'other' => array (99, 100),
),
)
)
That way, you can access things through mroe meaningful names like
$stuff['floor1']['motorcycle'][2]

In php you can have named keys for your arrays. Each element in your array can have different types so you could have
$floors = array(
1 => array(
'num_spots' => 100,
'handicap' => array(15,16,17)
),
2 => array(
'num_spots' => 100,
'handicap' => array(15,16,17),
'motorcycle' => array (47,62)
)
);
etc...

You can do exactly that. Example:
$a = array();
$a[1] = array(
'spots' => 100,
'handicap' => array(5,3,5)
);
$a[2] = array(
'spots' => 50,
'handicap' => array(1,3,20)
);
var_dump($a);
Output:
array
1 =>
array
'spots' => int 100
'handicap' =>
array
0 => int 5
1 => int 3
2 => int 5
2 =>
array
'spots' => int 50
'handicap' =>
array
0 => int 1
1 => int 3
2 => int 20
You can use the array indices for the floor number or have a separate key/value for that.

Something like...
$floors = array();
$floors[1] => array(
Spaces => 100,
Handicapped => array(15, 16, 17),
Motorcycle => array(47, 62),
Other => array (1, 2, 3, ..., n)
)
You can then retrieve the values as ...
$Floor1Spaces = $floors[1]['Spaces']; //An integer
$Floor1HAndicapped = $floors[1]['Handicapped']; //A 1-dimensional array of integer

Related

Sum of array values based on similar values from another array

This might be a little confusing, but I am going to explain it as best as I can. Please bear with me.
I have the following arrays:
Array
(
[question1] => 69
[question2] => 36
[question3] => 57
[question4] => 69
[question5] => 58
[question6] => 40
[question7] => 58
)
Array
(
[question1] => 8
[question2] => 6
[question3] => 5
[question4] => 6
[question5] => 7
[question6] => 8
[question7] => 5
)
As you can see the two arrays have identical keys, but different values for each key.
I need to find the keys in the second array that have the same values, so [question1] and [question6] both have a value of 8. And then in the first array I need to add together the values of [question1] and [question6] because they have a like value in the second array. I need to add the first array values together based on matching values in the second array (if that makes any sense)
Ideally, the output would be another array that would look something like this:
Array
(
[5] => 115
[8] => 109
[6] => 105
[7] => 58
)
Where the value of the second array becomes the key and the sum of the added values from the first array is the value.
Now I won't be picky here, so if we can't get it into that exact format then that is okay. I just need to be able to add together the values in the first array based on the similar values in the second array.
I hope this makes sense. If it doesn't please comment and I will do my best to explain further.
The simplest solution is to iterate over the second array. Lookup the key into the first array and if it exists then add the corresponding value from the first array into the result array, indexed by the value from the second array.
Something like this:
$array1 = array(
'question1' => 69,
'question2' => 36,
'question3' => 57,
'question4' => 69,
'question5' => 58,
'question6' => 40,
'question7' => 58,
);
$array2 = array(
'question1' => 8,
'question2' => 6,
'question3' => 5,
'question4' => 6,
'question5' => 7,
'question6' => 8,
'question7' => 5,
);
// Compose the desired result here
$result = array();
// Iterate over the second array; its values become keys in the result array
foreach ($array2 as $key => $val) {
// If this is the first time when this value is reached then a corresponding
// value does not yet exists in the result array; add it
if (! isset($result[$val])) {
$result[$val] = 0;
}
// Lookup the key into the first array
if (isset($array1[$key])) {
// If it exists then add its value to the results
$result[$val] += $array1[$key];
}
}
// That's all
print_r($result);

Group row data within a 2d array based on a single column and push unique data into respective subarrays

I need to group data in a multidimensional array data which can be related on a single column entry_id.
In addition to the entry_id there are other columns that will also be identical in other related rows (ic, name, and residency). While grouping, these data points can simply be overwritten -- in other words, I don't need to collect multiple copies of the same value within the respective group.
Finally, there will be data points within respective groups that will differ -- these values need to stored as subarrays within the groups so that no data is lost.
If there is only one row's data in a group, the file_no and detail data does not need to be converted to an array. This means that the result array will have variable depth -- some rows will flat and other may be 2 dimensional.
Sample input:
$array = [
[
'entry_id' => 1,
'ic' => 2147483647,
'name' => 'Kořínková Blanka',
'residency' => 'Štětí, Lukešova 354, 411 08',
'file_no' => 'KSUL 77 INS 18898 / 2013',
'detail' => '749371da-725c-4738-8def-2f7167142a6f'
],
[
'entry_id' => 1,
'ic' => 2147483647,
'name' => 'Kořínková Blanka',
'residency' => 'Štětí, Lukešova 354, 411 08',
'file_no' => 'KSUL 77 INS 21218 / 2013',
'detail' => '43b6a718-4647-451d-9c53-50dfee8403ff'
],
[
'entry_id' => 2,
'ic' => 46900217,
'name' => 'ENTEC a.s. "v likvidaci"',
'residency' => 'Staré Město, Brněnská 1916, 686 03',
'file_no' => 'KSBR 28 INS 1232 / 2013',
'detail' => 'e2155a52-c464-4357-b71b-4f4ff75585eb'
],
];
Desired output (based on same 'entry_id' grouping):
Array
(
[0] => Array
(
[entry_id] => 1
[ic] => 2147483647
[name] => Kořínková Blanka
[residency] => Štětí, Lukešova 354, 411 08
[file_no] => Array
(
[0] => KSUL 77 INS 18898 / 2013
[1] => KSUL 77 INS 21218 / 2013
)
[detail] => Array
(
[0] => A749371da-725c-4738-8def-2f7167142a6f
[1] => 43b6a718-4647-451d-9c53-50dfee8403ff
)
)
[1] => Array
(
[entry_id] => 2
[ic] => 46900217
[name] => ENTEC a.s. "v likvidaci"
[residency] => Staré Město, Brněnská 1916, 686 03
[file_no] => KSBR 28 INS 1232 / 2013
[detail] => e2155a52-c464-4357-b71b-4f4ff75585eb
)
)
Your issue can be resolved with one functional block, using array_reduce() and array_merge() principles:
$mergeId = 'entry_id';
$data = array_reduce($data, function($c, $x) use ($mergeId)
{
$c[$x[$mergeId]] = isset($c[$x[$mergeId]])
?array_combine(
$z=array_keys($c[$x[$mergeId]]),
array_map(function($y) use ($x, $c, $mergeId)
{
return in_array($x[$y], (array)$c[$x[$mergeId]][$y])
?$c[$x[$mergeId]][$y]
:array_merge((array)$c[$x[$mergeId]][$y], [$x[$y]]);
}, $z)
)
:$x;
return $c;
}, []);
you may want to apply array_values() if you need to re-index result set (so keys would be consecutive, starting from 0). Check the fiddle.
Using a nested loop to iterate a single array for potential matches is an indirect "brute force" technique -- there are some fringe cases where this can be tolerated, but in this case the more performant and professional approach will be to assign temporary first level keys as you iterate each row. The new associative arrays permit simple/swift searching because of the way that PHP handles arrays/keys.
When an entry_id value is encountered for the first time, simply save the whole row. When an entry_id value is encountered after the first time, the scalar-typed file_no and detail elements need to be converted to array-type . This can be done manually, but array_merge_recursive() offers this "magic" natively.
Call array_values() after finished iterating if you do not want to keep the first level grouping keys.
Code: (Demo)
$result = [];
foreach ($array as $row) {
if (!isset($result[$row['entry_id']])) {
$result[$row['entry_id']] = $row;
} else {
$result[$row['entry_id']] = array_merge_recursive(
$result[$row['entry_id']],
['file_no' => $row['file_no'], 'detail' => $row['detail']]
);
}
}
var_export(array_values($result));
Manually re-casting elements within groups (Demo)
$result = [];
foreach ($array as $row) {
if (!isset($result[$row['entry_id']])) {
$result[$row['entry_id']] = $row;
} else {
$result[$row['entry_id']]['file_no'] = (array) $result[$row['entry_id']]['file_no']; // re-cast as single-element array
$result[$row['entry_id']]['detail'] = (array) $result[$row['entry_id']]['detail']; // re-cast as single-element array
$result[$row['entry_id']]['file_no'][] = $row['file_no']; // push new element into subarray
$result[$row['entry_id']]['detail'][] = $row['detail']; // push new element into subarray
}
}
var_export(array_values($result));

What does PHP syntax $var1[] = $var2 mean?

What does PHP syntax $var1[] = $var2 mean?
Note the [] after $var1 varable.
It means that $var1 is an array, and this syntax means you are inserting $var2 into a new element at the end of the array.
So if your array had 2 elements before, like this:
$var1=( 1 => 3, 2 => 4)
and the value of $var2 was 5, it would not look like this:
$var1=( 1 => 3, 2 => 4, 3 => 5)
It also means that if $var2 was an array itself, you have just created a two dimensional array. Assuming the following:
$var1=( 1 => 3, 2 => 4)
$var2=( 1 => 10, 2=>20)
doing a $var1[]=$var2; wouls result in an array like this:
$var1=( 1 => 3, 2 => 4, 3 => (1 =>10, 2 => 20))
As an aside, if you haven't used multi-dimensional arrays yet, accessing the data is quite simple. If I wanted to use the value 20 from our new array, I could do it in this manner:
echo $var1[3][2];
Note the second set of square brackets - basically you are saying I want to access element two of the array inside element 3 of the $var1 array.
On that note, there is one thing to be aware of. If you are working with multi-dimensional arrays, this syntax can catch you out inside a loop structure of some sort. Lets say you have a two dimensional array where you store some records and want to get more from the database:
$var1 = (
1 => (id =>1, age => 25, posts => 40),
2 => (id =>2, age => 29, posts => 140),
3 => (id =>3, age => 32, posts => 12)
)
A loop like this following:
while($row=someDatabaseRow)
{
$var1[]=$row['id']; // value 4
$var1[]=$row['age']; // value 21
$var1[]=$row['posts']; // value 34
}
will infact insert a new element for every execution, hence your array would end up looking like this:
$var1 = (
1 => (id =>1, age => 25, posts => 40),
2 => (id =>2, age => 29, posts => 140),
3 => (id =>3, age => 32, posts => 12),
4 => 4,
5 => 21,
6 => 34
)
The correct way would be to assemble the array first, then append it to your current array to maintain the strucutre, like this:
while($row=someDatabaseRow)
{
$tempArr= array();
$tempArr[]=$row['id']; // value 4
$tempArr[]=$row['age']; // value 21
$tempArr[]=$row['posts']; // value 34
$var1[]=$tempArr;
}
Now your array would look like you expected, namely:
$var1 = (
1 => (id =>1, age => 25, posts => 40),
2 => (id =>2, age => 29, posts => 140),
3 => (id =>3, age => 32, posts => 12)
4 => (id =>4, age => 21, posts => 34)
)
It means that you're pushing the value of var2 to array var1.
That's shorthand for array_push()
It will make $var1 and array (if it isn't already) and push $var2 in to it.
It basically makes $var1 an array and adding to it's end a value of $var2.
$var1 = NULL;
$var2 = 'abc';
$var1[] = $var2;
$var1 is now an array containing one value: 'abc'

PHP custom ordering array

An array arrives with some or all of the following values, in any order. What's the best way to order them in ascending size order? So starting with small and ending with XXL. I can usort but am a bit lost as to how the elements should be ordered in my user defined function
Small
XXL
Medium
Large
XL
EDIT: left out some info so created new question Custom ordering array with key / value pairs
EDIT2: Full code
print_r($sizes);
$sorted_sizes = $this->sort_sizes(array_unique($sizes));
print_r($sorted_sizes);
function sort_sizes($sizes)
{
return uasort($sizes, array($this, 'cmp'));
}
function cmp($a,$b)
{
$sizeArray = array( 'Small' => 0, 'Medium' => 1, 'Large' => 2, 'XL' => 3, 'XXL' => 4);
return $sizeArray[$a] - $sizeArray[$b];
}
This outputs:
Array
(
[66-507cddcd16d9786abafccfa78b19acf8] => XL
[64-507cddcd16d9786abafccfa78b19acf8] => medium
[65-507cddcd16d9786abafccfa78b19acf8] => large
[63-507cddcd16d9786abafccfa78b19acf8] => small
)
and print_r($sorted_sizes) just gives output "1"
Updated answer according to full code
The first issue here is that you're returning the result of uasort():
function sort_sizes($sizes)
{
return uasort($sizes, array($this, 'cmp'));
}
That's wrong, because uasort() does not return the sorted array. It modifies the same variable that you pass as a parameter, and returns a boolean value. That's why you see 1 as output.
Make the method accept $sizes by reference:
function sort_sizes(array &$sizes)
{
uasort($sizes, array($this, 'cmp'));
}
Then call it like so:
print_r($sizes);
$sorted_sizes = array_unique($sizes);
$this->sort_sizes($sorted_sizes);
print_r($sorted_sizes);
Here's your cmp() method, with added support for case-insensitive sorting:
function cmp($a, $b)
{
$sizes = array('small' => 0, 'medium' => 1, 'large' => 2, 'xl' => 3, 'xxl' => 4);
return $sizes[strtolower($a)] - $sizes[strtolower($b)];
}
Old answer
Try this. Use uasort() instead if you want to maintain key-value pairs:
function sort_sizes($a, $b) {
// Map the sizes to an ordered sequence of ints
static $sizes = array('small' => 0, 'medium' => 1, 'large' => 2, 'xl' => 3, 'xxl' => 4);
// Find the difference, using the sizes as keys to the above array
return $sizes[strtolower($a)] - $sizes[strtolower($b)];
}
$arr = array('Small', 'XXL', 'Medium', 'Large', 'XL');
print_r($arr); // Before sorting
uasort($arr, 'sort_sizes');
print_r($arr); // After sorting
Output:
Array
(
[0] => Small
[1] => XXL
[2] => Medium
[3] => Large
[4] => XL
)
Array
(
[0] => Small
[2] => Medium
[3] => Large
[4] => XL
[1] => XXL
)
You can do this using array_multisort:
$orderIndex = array_flip(array('Small','Medium','Large','XL','XXL'));
$arr = array('Small','XXL','Medium','Large','XL');
array_multisort(array_map(function($val) use ($orderIndex) { return $orderIndex[$val]; }, $arr), $arr);
Here array_map is used with an anonymous function to build an array of the “weight” of each value in $arr. That array is then used to order the values in $arr.
This is basically the same as BoltClock suggested just with doing the comparison on the already calculated “weights”.
Do the sorting in two phases. First, convert text to a convenient numeric value. In this moment you should decide if "L" and "Large" map to the same value or not. Then reorder the array based on those numeric conversions.

get only 5 elements from array

my array is setup as follow:
array
'testuri/abc' =>
array
'label' => string 'abc' (length=3)
'weight' => float 5
'testuri/abd' =>
array
'label' => string 'abd' (length=3)
'weight' => float 2
'testuri/dess' =>
array
'label' => string 'dess' (length=4)
'weight' => float 2
'testuri/gdm' =>
array
'label' => string 'gdm' (length=3)
'weight' => float 2
'testuri/abe' =>
array
'label' => string 'abe' (length=3)
'weight' => float 2
'testuri/esy' =>
array
'label' => string 'esy' (length=3)
'weight' => float 2
'testuri/rdx' =>
array
'label' => string 'rdx' (length=3)
'weight' => float 3
'testuri/tfc' =>
array
'label' => string 'tfc' (length=3)
'weight' => float 3
I want to get/filter the 5 elements with bigges 'weight'. Is there a php function to make this?
PS. My idea was to use foreach
Sort the array by the weight value in descending order and then get the first five values:
function cmpByWeight($a, $b) {
return $b['weight'] - $a['weight'];
}
uasort($array, 'cmpByWeight');
$firstFive = array_slice($array, 0, 5);
You'd be better using uasort with a callback that compares the 'weight' index of the passed values, and then array_slice to grab the first 5 elements (or last 5 depending on which way you sort...)
As far as I know there isn't.
You could use a heap for this, but for only 5 elements I'm not sure it's faster than just storing the top 5.
I would use array_multisort() and then grab the first 5 values.
array_multisort($weight, SORT_DESC, $label, SORT_ASC, $YOUR_ARRAY)
Then just grab $YOUR_ARRAY[0] - $YOUR_ARRAY[4] or iterate over the array to grab the first 5
[EDIT] here's a link to the function --> http://us3.php.net/manual/en/function.array-multisort.php
My English is not the best, i will try to explain, what i need. i can sort the array....but in the example above i have following:
1x weight 5
2x weight 3
5x weight 2
So...if I grab the first 5 elements, the another 3 with weight 2 will be ignored. So i need all 5 elements with weight 2....and so i have an array with 7 items.
Another example:
2x weight 5
4x weight 2
7x weight 1
All elements with weight1 must be ignored, So i get 6 elements in a new array..

Categories