with a normal array one would use in_array() , but in_array doesn't support SplFixedArray, and i couldn't really find any splFixedArray in_array() equivalent, i could just foreach-loop it myself like
function spl_in_array(\SplFixedArray &$arr, $value): bool
{
foreach ($arr as $val) {
if ($val === $value) {
return true;
}
}
return false;
}
but that appears to be ~14-17 times slower than in_array(), and since the whole point of SplFixedArray is performance and memory usage (i think?)... with this benchmark code
<?php
declare(strict_types = 1);
$check_iterations = 100;
$nbr_array_values = 100000;
$lookup_value = round($nbr_array_values / 2);
function format(float $f): string
{
return number_format($f, 6);
}
function spl_in_array(\SplFixedArray &$arr, $value): bool
{
foreach ($arr as $val) {
if ($val === $value) {
return true;
}
}
return false;
}
$arr = [];
for ($i = 0; $i < $nbr_array_values; ++ $i) {
$arr[] = $i;
}
$splArr = SplFixedArray::fromArray($arr);
$results = [];
$start = microtime(true);
for ($i = 0; $i < $check_iterations; ++ $i) {
in_array($lookup_value, $arr, true);
}
$end = microtime(true);
$results["in_array"] = $end - $start;
$start = microtime(true);
for ($i = 0; $i < $check_iterations; ++ $i) {
spl_in_array($splArr, $lookup_value);
}
$end = microtime(true);
$results["spl_in_array"] = ($end - $start);
$results["in_array/spl_in_array"] = $results["in_array"] / $results["spl_in_array"];
$results["spl_in_array/in_array"] = $results["spl_in_array"] / $results["in_array"];
$fastest = ($results["in_array"] > $results["spl_in_array"]) ? "spl_in_array" : "in_array";
foreach ($results as &$result) {
$result = format($result);
}
$results["fastest"] = $fastest;
print_r($results);
i get
Array
(
[in_array] => 0.001330
[spl_in_array] => 0.019912
[in_array/spl_in_array] => 0.066801
[spl_in_array/in_array] => 14.969887
[fastest] => in_array
)
Array
(
[in_array] => 0.001024
[spl_in_array] => 0.015516
[in_array/spl_in_array] => 0.065997
[spl_in_array/in_array] => 15.152270
[fastest] => in_array
)
Array
(
[in_array] => 0.001124
[spl_in_array] => 0.015159
[in_array/spl_in_array] => 0.074140
[spl_in_array/in_array] => 13.487908
[fastest] => in_array
)
Array
(
[in_array] => 0.000838
[spl_in_array] => 0.013853
[in_array/spl_in_array] => 0.060495
[spl_in_array/in_array] => 16.530299
[fastest] => in_array
)
Array
(
[in_array] => 0.000960
[spl_in_array] => 0.014201
[in_array/spl_in_array] => 0.067609
[spl_in_array/in_array] => 14.790911
[fastest] => in_array
)
Array
(
[in_array] => 0.000865
[spl_in_array] => 0.015183
[in_array/spl_in_array] => 0.056970
[spl_in_array/in_array] => 17.553197
[fastest] => in_array
)
so doing it manually with a foreach() is evidently not a particularly efficient way of doing it..
hence the question, how should i check if a SplFixedArray contains something? what is the SplFixedArray-equivalent of in_array() ?
If you make use of the ->toArray() method to convert the spl array to a standard PHP Array and then use in_array() you can reduce the time enormously
$check_iterations = 100;
$nbr_array_values = 100000;
$lookup_value = round($nbr_array_values / 2);
function format(float $f): string
{
return number_format($f, 6);
}
function spl_in_array(\SplFixedArray &$arr, $value): bool
{
foreach ($arr as $val) {
if ($val === $value) {
return true;
}
}
return false;
}
function new_spl_in_array(\SplFixedArray &$arr, $value): bool
{
$a = $arr->toArray();
return in_array($value, $a);
}
$arr = [];
for ($i = 0; $i < $nbr_array_values; ++ $i) {
$arr[] = $i;
}
$splArr = SplFixedArray::fromArray($arr);
$results = [];
$start = microtime(true);
for ($i = 0; $i < $check_iterations; ++ $i) {
in_array($lookup_value, $arr, true);
}
$end = microtime(true);
$results["in_array"] = $end - $start;
$start = microtime(true);
for ($i = 0; $i < $check_iterations; ++ $i) {
spl_in_array($splArr, $lookup_value);
}
$end = microtime(true);
$results["spl_in_array"] = ($end - $start);
$start = microtime(true);
for ($i = 0; $i < $check_iterations; ++ $i) {
new_spl_in_array($splArr, $lookup_value);
}
$end = microtime(true);
$results["spl_in_array_new"] = ($end - $start);
$results["in_array/spl_in_array"] = $results["in_array"] / $results["spl_in_array"];
$results["spl_in_array/in_array"] = $results["spl_in_array"] / $results["in_array"];
$fastest = ($results["in_array"] > $results["spl_in_array"]) ? "spl_in_array" : "in_array";
foreach ($results as &$result) {
$result = format($result);
}
$results["fastest"] = $fastest;
print_r($results);
RESULTS
Array
(
[in_array] => 0.011569
[spl_in_array] => 1.094222
[spl_in_array_new] => 0.157041 // < Using ->toArray()
[in_array/spl_in_array] => 0.010573
[spl_in_array/in_array] => 94.583991
[fastest] => in_array
)
Related
I am looking to store sum of all keys inside an array here is my example code
<?php
// Sample data
$category = (object) ['category_name' => '32459*1500*lab*1,32460*400*lab*1,32461*600*lab*1'];
// process
$category_sale_data = explode(',', $category->category_name);
foreach ($category_sale_data as $key => $value) {
list($sale_key, $sale_value) = explode('*', $value);
$category->sale_data[$sale_key][] = $sale_value;
//$category->sale_data_sum[$sale_key][] += $sale_value;
}
// display
print_r($category);
getting this output working example -> https://3v4l.org/NAKfb#v7.0.0
Here is expected to get //$category->sale_data_sum[$sale_key][] +=
$sale_value;
I am expected output like this
stdClass Object
(
[category_name] => 32459*1500*lab*1,32460*400*lab*1,32461*600*lab*1
[sale_data] => Array
(
[32459] => Array
(
[0] => 1500
)
[32460] => Array
(
[0] => 400
)
[32461] => Array
(
[0] => 600
)
)
[sale_data_sum] => 2500
)
Simply do this:
$category->sale_data_sum = 0; // initiate key
foreach ($category_sale_data as $key => $value) {
list($sale_key, $sale_value) = explode('*', $value);
$category->sale_data[$sale_key][] = $sale_value;
$category->sale_data_sum += $sale_value; // add each sale value
}
$category = [ 'category_name' => '32459*1500*lab*1,32460*400*lab*1,32461*600*lab*1' ];
// category_name
$result['category_name'] = $category['category_name'];
// sale_data
$splitted = preg_split('/[*,]/', $category['category_name']);
for($i = 0; $i < count($splitted); $i += 4) {
$result['sale_data'][$splitted[$i]] = $splitted[$i + 1];
}
// sale_data_sum
$result['sale_data_sum'] = array_sum($result['sale_data']);
print_r($result);
Try this
<?php
// Sample data
$category = (object) ['category_name' => '32459*1500*lab*1,32460*400*lab*1,32461*600*lab*1'];
// process
$category_sale_data = explode(',', $category->category_name);
foreach ($category_sale_data as $key => $value) {
list($sale_key, $sale_value) = explode('*', $value);
$category->sale_data[$sale_key][] = $sale_value;
//$category->sale_data_sum[$sale_key][] += $sale_value;
}
function sum($carry, $item)
{
$carry += array_values($item)[0];
return $carry;
}
$a = array_reduce(array_values($category->sale_data), "sum");
var_dump($a);
Or
<?php
// Sample data
$category = (object) ['category_name' => '32459*1500*lab*1,32460*400*lab*1,32461*600*lab*1'];
// process
$category_sale_data = explode(',', $category->category_name);
$category->sale_data_sum = null;
foreach ($category_sale_data as $key => $value) {
list($sale_key, $sale_value) = explode('*', $value);
$category->sale_data[$sale_key][] = $sale_value;
$category->sale_data_sum += $sale_value;
}
// display
print_r($category);
I have a two-dimensional array containing events and dates:
$events = array(
array('event' => 'event1', 'date' => '2016-05-05'),
array('event' => 'event2', 'date' => '2016-05-08'),
array('event' => 'event3', 'date' => '2016-05-08'),
array('event' => 'event4', 'date' => '2016-05-10'),
array('event' => 'event5', 'date' => '2016-05-10'),
array('event' => 'event6', 'date' => '2016-05-11'),
array('event' => 'event7', 'date' => '2016-05-11'),
array('event' => 'event8', 'date' => '2016-05-13')
};
Let's say today is 2016-05-10 and I want to create 3 new arrays:
$recent - all events happened on the previous available day
$today - all events today
$upcoming - all events happening on the next available day
$today is easy:
$today = array();
for($i = 0; $i < count($events); ++$i) {
if($events[$i]['date'] == date("Y-m-d") {
array_push($today, $events[$i]['event']);
}
}
So, I will need $recent to contain event2 and event3, and $upcoming to contain event6 and event7.
The question is how to find the recent and the upcoming ones.
*Clarification:
I don't want all the events in $recent happened before today, but the events happened on the previous available day. So in this case only events happened on 2016-05-08
// Take all dates from source array
$dates = array_unique(array_map(function ($i) { return strtotime($i); } , array_column($events, 'date')));
sort($dates);
$today = strtotime('midnight');
// find previouse date. It will be 1970-1-1 if not present in array
$prev = #max(array_filter($dates, function($i) use($today) { return $i < $today; }));
// find ัััะต date. It will be 1970-1-1 if not present in array
$next = #min(array_filter($dates, function($i) use($today) { return $i > $today; }));
$prev = date('Y-m-d', $prev);
$next = date('Y-m-d', $next);
// fill arrays
$recent = array();
$upcoming = array();
$today = array();
for($i = 0; $i < count($events); ++$i) {
if($events[$i]['date'] == date('Y-m-d')) {
array_push($today, $events[$i]['event']);
}
if($events[$i]['date'] == $prev) {
array_push($recent, $events[$i]['event']);
}
if($events[$i]['date'] == $next) {
array_push($upcoming , $events[$i]['event']);
}
}
Try with creating three (3) blank array, check the date is greater or less than today or not and push into array according to conditions.
$today = array();
$upcoming = array();
$recent = array();
$thisDay = date("Y-m-d");
$count = count($events);
$max = max(array_column($events, 'date'));
$min = min(array_column($events, 'date'));
for($i = 0; $i < $count; $i++){
if($events[$i]['date'] > $thisDay){
$max = ($max > $events[$i]['date']) ? $events[$i]['date'] : $max;
array_push($upcoming, $events[$i]['event']);
array_push($upcoming_dates, $events[$i]['date']);
}
elseif($events[$i]['date'] < $thisDay){
$min = ($min < $events[$i]['date']) ? $events[$i]['date'] : $min;
array_push($recent, $events[$i]['event']);
array_push($recent_dates, $events[$i]['date']);
}
else
array_push($today, $events[$i]['event']);
}
foreach($recent_dates as $key => $value){
if($value != $min)
unset($recent[$key]);
}
foreach($upcoming_dates as $key => $value){
if($value != $max)
unset($upcoming[$key]);
}
echo '<pre>';
print_r($today);
print_r($upcoming);
print_r($recent);
Result
Today:
Array
(
[0] => event4
[1] => event5
)
Upcoming:
Array
(
[0] => event6
[1] => event7
)
Recent:
Array
(
[1] => event2
[2] => event3
)
Note: You use push_array which is not any library function in PHP. For Re-index of recent and upcoming you can use array_values.
Another solution should be
$recent = array();
$upcoming = array();
$today = array();
$all_dates = array();
foreach ($events as $event):
array_push($all_dates, $event['date']);
endforeach;
if ($key = array_search('2016-05-10', $all_dates)) {
$prev_date = $all_dates[$key - 1];
}
for ($i = 0; $i < count($all_dates); $i++) {
if ($all_dates[$i] > $all_dates[$key]) {
$next_date = $all_dates[$i + 1];
break;
}
}
for ($i = 0; $i < count($events); ++$i) {
if ($events[$i]['date'] == date('Y-m-d')) {
array_push($today, $events[$i]['event']);
}
if ($events[$i]['date'] == $prev_date) {
array_push($recent, $events[$i]['event']);
}
if ($events[$i]['date'] == $next_date) {
array_push($upcoming, $events[$i]['event']);
}
}
echo '<pre>';
print_r($upcoming);
print_r($today);
print_r($recent);
Output will be
Array
(
[0] => event6
[1] => event7
)
Array
(
[0] => event4
[1] => event5
)
Array
(
[0] => event2
[1] => event3
)
Please help me to extract and display the values from this array..
This is the output when I do a print_r($array); :
Array
(
[0] => SPD Object
(
[DRIVERNAME] => SPD Barry
[STARTTIME] => 07:44
[FINISHTIME] => 18:12
[STOP] =>
[SEQUENCENO] => 37
[PLACENAME] => AMSTERDAM ZUIDOOST
[ARRIVALTIME] => 17:32
)
[1] => SPD Object
(
[DRIVERNAME] => SPD Brady
[STARTTIME] => 07:36
[FINISHTIME] => 15:53
[STOP] =>
[SEQUENCENO] => 32
[PLACENAME] => NIEUWEGEIN
[ARRIVALTIME] => 15:30
)
[2] => SPD Object
(
[DRIVERNAME] => SPD Bram
[STARTTIME] => 08:11
[FINISHTIME] => 18:32
[STOP] =>
[SEQUENCENO] => 32
[PLACENAME] => LAGE ZWALUWE
[ARRIVALTIME] => 17:28
)
)
What I want to do is, get this Driver Name and Sequence Number and echo them.
UPDATE :
My full code can be found below.
I get a xml file which contain this kind of stuff :
<TRIP>
<DRIVERNAME>SPD Barry</DRIVERNAME>
<STARTTIME>07:44</STARTTIME>
<FINISHTIME>18:12</FINISHTIME>
<STOP>
<SEQUENCENO>1</SEQUENCENO>
<PLACENAME>ROTTERDAM</PLACENAME>
<ARRIVALTIME>08:30</ARRIVALTIME>
</STOP>
</TRIP>
Here is my PHP file to collect data into an array.
<?php
class SPD {
function SPD ($aa) {
foreach ($aa as $k=>$v)
$this->$k = $aa[$k];
}
}
function readDatabase($filename) {
// read the XML database of aminoacids
$data = implode("", file($filename));
$parser = xml_parser_create();
xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
xml_parse_into_struct($parser, $data, $values, $tags);
xml_parser_free($parser);
// loop through the structures
foreach ($tags as $key=>$val) {
if ($key == "TRIP") {
$molranges = $val;
// each contiguous pair of array entries are the
// lower and upper range for each molecule definition
for ($i=0; $i < count($molranges); $i+=2) {
$offset = $molranges[$i] + 1;
$len = $molranges[$i + 1] - $offset;
$tdb[] = parseMol(array_slice($values, $offset, $len));
}
} else {
continue;
}
}
return $tdb;
}
function parseMol($mvalues) {
for ($i=0; $i < count($mvalues); $i++) {
$mol[$mvalues[$i]["tag"]] = $mvalues[$i]["value"];
}
return new SPD($mol);
}
$db = readDatabase("test.xml");
if(is_array($db)){
foreach($db as $item) {
echo $item->DRIVERNAME;
echo $item->SEQUENCENO;
}
}
?>
What I want to do is, echo Driver name and Sequence Number :)
This should do it :
<?php
class SPD {
function SPD($aa) {
foreach ($aa as $k => $v)
$this->$k = $aa[$k];
}
}
function readDatabase($filename) {
// read the XML database of aminoacids
$data = implode("", file($filename));
$parser = xml_parser_create();
xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
xml_parse_into_struct($parser, $data, $values, $tags);
xml_parser_free($parser);
// loop through the structures
foreach ($tags as $key => $val) {
if ($key == "TRIP") {
$molranges = $val;
// each contiguous pair of array entries are the
// lower and upper range for each molecule definition
for ($i = 0; $i < count($molranges); $i+=2) {
$offset = $molranges[$i] + 1;
$len = $molranges[$i + 1] - $offset;
$tdb[] = parseMol(array_slice($values, $offset, $len));
}
} else {
continue;
}
}
return $tdb;
}
function parseMol($mvalues) {
for ($i = 0; $i < count($mvalues); $i++) {
if(isset($mvalues[$i]["value"])) {
$mol[$mvalues[$i]["tag"]] = $mvalues[$i]["value"];
}
}
return new SPD($mol);
}
$db = readDatabase("test.xml");
if (is_array($db)) {
foreach ($db as $item) {
echo 'ITEM 1 : ' . "\n<br/>";
echo '---------------' . "\n<br/>";
echo 'DRIVERNAME : ' . $item->DRIVERNAME . "\n<br/>";
echo 'SEQUENCENO : ' . $item->SEQUENCENO . "\n\n<br/><br/>";
}
}
?>
I have an array like this one :
Array
{
'property1.subproberty11' => "xxxxx",
'property1.subproberty12' => "yyyy",
'property2.subproberty21.subproperty211' => "zzzzzz",
'property2.subproberty21.subproperty212' => "wwwww",
'property2.subproberty22' => "yyyy",
....
That needs to be changed into something like :
Array
(
[property1] => Array
(
['subproberty11'] => "xxxxx"
['subproberty12'] => "yyyy"
)
[property2] => Array
(
[subproperty21] => Array
(
[subproperty211] => "zzzzzz"
[subproperty212] => "wwwwww"
)
['subproberty22'] => "yyyy"
)
...
I can't find a smart way of doing this, can someone help me ?
So, far, i have thought of this kind of algorithm :
$new_array = array();
foreach($old_array as $key => $value)
{
$subkeys = explode('.', $key);
$ss = array();
for($ii = 0 ; $ii < count($subkeys) ; $ii++)
{
$ss[] = "['".$subkeys[$ii]."']";
if ($ii < count($subkeys) -1)
eval('$new_array'.implode('',$ss).' = array();');
}
eval('$new_array'.implode('',$ss)." = '".$value."';');
}
I think we can do better, for example maybe we can avoid duplicating data by creating a new array ?
My working example:
function nestedKeysArray($input) {
$array = array();
foreach ($input as $key => $value) {
$keys = explode('.', $key);
if (count($keys) == 1) {
$array[$key] = $value;
} else {
$nested = &$array;
foreach ($keys as $k) {
if (!isset($nested[$k]))
$nested[$k] = array();
$nested = &$nested[$k];
}
$nested = $value;
}
}
return $array;
}
$input is array like first one from the question.
EDIT:
Changing original array, without copy:
function nestedKeysArray(&$input) {
foreach ($input as $key => $value) {
$keys = explode('.', $key);
if (count($keys) > 1) {
$nested = &$input;
foreach ($keys as $k) {
if (!isset($nested[$k]))
$nested[$k] = array();
$nested = &$nested[$k];
}
$nested = $value;
unset($input[$key]);
}
}
}
Some untested code, to give you the direction you could take.
Just loop through the array;
function SplitArray($properties) {
foreach($properties as $item=>$property) {
$properties[$item] = explode('.',$property, 2);
if(strpos($properties[$item][1], '.') === false)) {}
else {
$properties[$item][1] = SplitArray($properties[$item][1]);
}
}
return $properties;
}
I need to extract a associative array keys into a string and implode with "/" or any character/symbols.
For eg:
$array = array([key1] =>
array([key11] =>
array([key111] => 'value111',
[key112] => 'value112',
[key113] => 'value113',
),
),
);
I need an output as below array:
array([0] => 'key1/key11/key111',[1] => 'key1/key11/key112', [2] => 'key1/key11/key112');
I've edited an answer given here and came up with the following code.
function listArrayRecursive($someArray, &$outputArray, $separator = "/") {
$iterator = new RecursiveIteratorIterator(new RecursiveArrayIterator($someArray), RecursiveIteratorIterator::SELF_FIRST);
foreach ($iterator as $k => $v) {
if (!$iterator->hasChildren()) {
for ($p = array(), $i = 0, $z = $iterator->getDepth(); $i <= $z; $i++) {
$p[] = $iterator->getSubIterator($i)->key();
}
$path = implode($separator, $p);
$outputArray[] = $path;
}
}
}
$outputArray = array();
listArrayRecursive($array, $outputArray);
print_r($outputArray);
Input:
Array
(
[key1] => Array
(
[key11] => Array
(
[key111] => value111
[key112] => value113
[key113] => value113
)
)
)
Output:
Array
(
[0] => key1/key11/key111
[1] => key1/key11/key112
[2] => key1/key11/key113
)
Works for different depth of array:
function getKeys($array, $prefix='', $separator = '/') {
$return = array();
foreach($array as $key => $value) {
if (!is_array($value)) $return[] = $prefix . $key;
else $return = array_merge($return, getKeys($value, $prefix . $key . separator), $separator);
}
return $return;
}
$keys = getKeys($array, '', '#');
See online fiddle http://ideone.com/krU4Xn
you could do something like...
$mapArray = array();
$symbol = '/';
foreach($array as $k =>$v)
foreach($v as $k1 =>$v1)
foreach($v1 as $k2 =>$v2)
$mapArray[] = $k.$symbol.$k1.$symbol.$k2;
also this obviously only works in this particular case, if it needs to be more generic it can be done, but I think this should get you started.