substitute the elements of an array coditionally - php

I need to create an array of prices. So I've an array of base prices and an array of promotional prices, if the promotional price is lower than the base price, I put it in the new array of prices. For this purpose I wrote this code:
<?php
$array_base_prices = array(
array('id'=>1, 'price'=>10),
array('id'=>2, 'price'=>2),
array('id'=>3, 'price'=>30),
array('id'=>4, 'price'=>40)
);
$array_promo = array(
array('id'=>1, 'price'=>20),
array('id'=>2, 'price'=>5),
array('id'=>3, 'price'=>2)
);
$base_prices_with_promo = array();
foreach ( $array_base_prices as $j => $u ) {
foreach ( $array_promo as $k => $v ) {
if ( ($v['id'] == $u['id']) && $u['price'] < $v['price']) {
$base_prices_with_promo[$j]['id'] = $array_base_prices[$j]['id'];
$base_prices_with_promo[$j]['price'] = $array_base_prices[$j]['price'];
}
if ( ($v['id'] == $u['id']) && $u['price'] > $v['price']) {
$base_prices_with_promo[$j]['id'] = $array_promo[$k]['id'];
$base_prices_with_promo[$j]['price'] = $array_promo[$k]['price'];
}
}
}
$base_prices_with_promo = array_replace( $array_base_prices, $base_prices_with_promo );
echo "<pre>";
print_r($base_prices_with_promo);
echo "</pre>";
?>
and it works fine, but I think that the if conditions into the nested foreach are a little messy. I'm working with multidimensional, associative arrays, very big with a lot of keys. So I'm wondering if there is any better alternative to achieve the same result.

There's really not enough context for me to be sure that this is an option for you, but from your little example I would change the array declaration to this:
$array_base_prices = array(
1 => 10,
2 => 2,
3 => 30,
4 => 40
);
$array_promo = array(
1 => 20,
2 => 5,
3 => 2
);
Or use an array if you need to store more data than just price:
$array_base_prices = array(
1 => array("price" => 10, "something_else" => null)
);
The point is to have the id as index of the parent array. Then your nested loops turn to this:
foreach ($array_base_prices as $id => $base_price) {
$base_prices_with_promo[$id] = $base_price;
if (isset($array_promo[$id]) && $base_price > $array_promo[$id]) {
$base_prices_with_promo[$id] = $array_promo[$id];
}
}

Related

How to count the number of item in foreach loop based by it's parent?

I have a simple loop to display a list. But I do not know how to count it's parent item. This is my current attempt:
$no = 0;
$ct = 0;
$type = "";
foreach($item as $row_item){
$no = $no + 1;
if($type != $row_item['type']){
$ct = $ct + 1;
}
echo $no." ".$row_item['type']." ".$row_item['item'];
$type = $row_item['type'];
}
My desired output :
1 TYPE_A 3 A1
2 TYPE_A 3 A2
3 TYPE_A 3 A3
4 TYPE_B 2 B1
5 TYPE_B 2 B2
In order to count the total number of each type you'll need to iterate over the entire collection twice. Once to count the totals, and once to display the results for each row. The code below actually does 3 loops, the array_filter method iterates over the entire array, but I like the clean code. :)
http://sandbox.onlinephpfunctions.com/code/962f418715d1518c818732f6e59ba4f28d5a19f3
<?php
$items = array(
array( 'name' => 'A1', 'type' => 'TYPE_A' ),
array( 'name' => 'A2', 'type' => 'TYPE_A' ),
array( 'name' => 'A3', 'type' => 'TYPE_A' ),
array( 'name' => 'B1', 'type' => 'TYPE_B' ),
array( 'name' => 'B2', 'type' => 'TYPE_B' )
);
function is_TYPE_A( $item ) {
return $item['type'] == 'TYPE_A';
}
function is_TYPE_B( $item ) {
return $item['type'] == 'TYPE_B';
}
$TYPE_A_COUNT = count( array_filter( $items, 'is_TYPE_A' ) );
$TYPE_B_COUNT = count( array_filter( $items, 'is_TYPE_B' ) );
function getTypeTotalByItem( $item ) {
global $TYPE_A_COUNT, $TYPE_B_COUNT;
if ( $item['type'] == 'TYPE_A' ) {
return $TYPE_A_COUNT;
}
if ( $item['type'] == 'TYPE_B' ) {
return $TYPE_B_COUNT;
}
}
for ( $i = 0; $i < count( $items ); $i++ ) {
echo ( $i + 1 )." ".$items[$i]['type']." ".getTypeTotalByItem($items[$i])." ".$items[$i]['name']."\n";
}
You can use array_map() and a couple foreach() loops for this if you were so inclined. It should interpret fairly quickly:
# Create a storage array
$counter = [];
# Sort the main array into type
array_map(function($v) use (&$counter){
# Store the subs under the type
$counter[$v['type']][] = $v['item'];
},$items);
# Start counter
$i = 1;
# Loop through each type
foreach($counter as $title => $row){
# Count how many are under this type
$count = count($row);
# Loop the rows in the types arrays
foreach($row as $item) {
# Write increment, type, total count, item
echo $i." ".$title." ".$count." ".$item.'<br />';
$i++;
}
}

Search associative array of arrays. How?

My question is how can I search an array built this way? Basically there may be a need to repeat the key and this is what I got so far to maybe solve this. If the price is the same for 2 different items I cannot have 2 keys with the same value.
Please feel free to improve on array layout.
$price_list = array(
1 => array("9.99", "EA_WTRESRVD"),
2 => array("9.99", "EA_WTRESRV")
);
Provided there will never be any duplication of the second column, you can do this:
$search = "EA_WTRESRVD"; //value to search for
$price_list = array(
1 => array("9.99", "EA_WTRESRVD"),
2 => array("9.99", "EA_WTRESRV")
);
$array = array_column($price_list, 0, 1);
echo $array[$search];
I would suggest that if you have a unique product code (SKU), you should use this to index your array.
$products = [
'EA_WTRESRVD' => [
'name' => '...',
'price' => 9.99,
// ...
],
'EA_WTRESRV' => [
'name' => '...',
'price' => 9.99,
// ...
],
];
Then you can access the price of any product by it's SKU.
$price = $products['EA_WTRESRV']['price'];
Here's one way:
<?php
$price_list = [ 1 => array("9.99", "EA_WTRESRVD"),
2 => array("9.99", "EA_WTRESRV")];
$search = "EA_WTRESRV";
foreach ($price_list as $arr) {
if (in_array( $search, $arr )) {
echo $search;
}
}
The foreach iterates over the multidimensional array whose elements are each arrays. Each array is inspected by in_array() for the search term.
However, this is not the only way. If you wish to avoid in_array(), you could also code as follows:
<?php
$price_list = [ 1 => array("9.99", "EA_WTRESRVD"),
2 => array("9.99", "EA_WTRESRV")];
$search = "EA_WTRESRV";
$len = strlen($search);
foreach ($price_list as $arr) {
$val = array_values($arr);
foreach($val as $v) {
if ( ( strpos( $v,$search )) !== false) {
if ( strlen($v) == $len) {
echo "$search is in the price list.\n";
}
}
}
}

PHP : merge multiples arrays with common keys [duplicate]

This question already has answers here:
Merge two indexed arrays of indexed arrays based on first column value
(2 answers)
Closed 5 months ago.
I have 5 differents arrays in my script :
$array1 = array(
array( "id"=>"1", "title"=>"Batman" ),
array( "id"=>"2", "title"=>"Spiderman" ),
array( "id"=>"3", "title"=>"Titanic" ),
array( "id"=>"4", "title"=>"Dracula" ),
);
$array2 = array(
array( "id"=>"1", "releasedate"=>"1926" ),
array( "id"=>"2", "releasedate"=>"1956" ),
array( "id"=>"3", "releasedate"=>"2001" ),
array( "id"=>"4", "releasedate"=>"1982" ),
);
etc ...
As you see, info about movie number 1 is splitted on all the arrays (in fact -> 5 arrays). Then I would like to merge all my arrays to get something like this :
$array_FINAL = array(
array( "id"=>"1", "title"=>"Batman", "releasedate"=>"1926" ),
array( "id"=>"2", "title"=>"Spiderman", "releasedate"=>"1956" ),
array( "id"=>"3", "title"=>"Titanic", "releasedate"=>"2001" ),
array( "id"=>"4", "title"=>"Dracula", "releasedate"=>"1982" ),
);
I tried array_merge, array_combine, no good results. I also checked other topics on stackoverflow but no one help me (i may miss the one i need!)
Any help ? :)
EDIT : Sorry, i did would give a little more details ... rows in the arrays could be in misc. order, then according to my code example : the movie "Batman" can be in 1st row in the first array, but in the 3rd row in the second array...
If the order of both arrays is the same, you can simply use
$array_final = array_replace_recursive($array1, $array2);
However, if you want to merge them by the "id", you need to loop through them. A solution with the complexity O(m*n):
$array_final = array();
// loop through all movies in array1
foreach($array1 as $movie){
foreach($array2 as $movie_release) {
if ($movie['id'] == $movie_release['id']) {
$movie['releasedate'] = $movie_release['releasedate'];
break;
}
}
$array_final[] = $movie;
}
And a little less complex O(m+n):
// create arrays with the ID as key
$titles = array_column($array1, 'title', 'id');
$releasedates = array_column($array2, 'releasedate', 'id');
$array_final = array();
foreach($titles as $id => $title) {
$array_final[] = array(
'id' => $id,
'title' => $title,
'releasedate' => $releasedates[$id]
);
}
One of the solutions is:
$array1 = array(
array( "id"=>"1", "title"=>"Batman" ),
array( "id"=>"2", "title"=>"Spiderman" ),
array( "id"=>"3", "title"=>"Titanic" ),
array( "id"=>"4", "title"=>"Dracula" ),
);
$array2 = array(
array( "id"=>"1", "releasedate"=>"1926" ),
array( "id"=>"2", "releasedate"=>"1956" ),
array( "id"=>"3", "releasedate"=>"2001" ),
array( "id"=>"4", "releasedate"=>"1982" ),
);
// here we create pairs `id => releasedate`
$new_array2 = [];
foreach ($array2 as $v) {
$new_array2[$v['id']] = $v['releasedate'];
}
foreach ($array1 as &$value) {
// here we try to find key `$value['id']` in `$new_array2`
// and get it's value
if (isset($new_array2[$value['id']])) {
$value['releasedate'] = $new_array2[$value['id']];
}
}
If you're 100% sure that orders of ids are the same in both arrays you can just:
$i = 0;
foreach ($array1 as &$value) {
$value['releasedate'] = $array2[$i]['releasedate'];
$i++;
}
See this code prototype (it is simple and don't need to explanations I believe):
$fArr = [];
for ( $i = 1; $i <= 4; $i++ ) {
$fArr[] = [
'id' => $i,
'title' => $array1[$i - 1]['title'],
'releasedate' => $array2[$i - 1]['releasedate'],
];
}
Sandbox code example
Assuming all of your five arrays contain the id key, you can do this with a nested foreach loop.
foreach (array_merge($array1, $array2, $array3, $array4, $array5) as $item) {
foreach ($item as $key => $value) {
$result[$item['id']][$key] = $value;
}
}
The outer foreach iterates all the rows in all the arrays. The inner one loops over each column and assigns its value to the corresponding key in the result array.

Create a hierarchical list of array elements

I have a an array of products. Each product contains its category and subcategories listed in a hierarchical order:
Array
(
[product_id_1] => Array
(
[0] => Men
[1] => Sunglasses
[2] => Luxury
[3] => Ray-ban
)
[product_id_2] => Array
(
[0] => Women
[1] => Lenses
[2] => Casual
[3] => Gucci
)
[product_id_3] => Array
(
[0] => Men
[1] => Sunglasses
[2] => Casual
[3] => Prada
)
[...]
)
I want to create an unordered hierarchical HTML menu like so:
-Men
--Sunglasses
---Luxury
----Ray-ban
---Casual
----Prada
-Women
--Lenses
---Casual
----Gucci
The function should strip out repetitive categories and subcategories. This script returns the array of products that I've posted at the top:
<?php
function displayNestedMenu( $posts, $taxonomies ) {
foreach ( $posts as $post ) {
foreach ( $taxonomies as $key => $taxonomy ) {
$push = wp_get_object_terms( $post->ID, $taxonomy );
if ( !empty( $push ) ) {
$list[$post->ID][] = $push[0]->name;
}
}
}
return $list;
}
print_r( displayNestedMenu( $posts, $taxonomies ) );
?>
I imagine the solution should invoke the function inside the function but after trying a couple of methods I haven't succeeded yet. Any suggestions are appreciated!
Here is a simple idea :
$array = array(
'product_id_1' => array(
'Men',
'Sunglasses',
'Luxury',
'Ray-ban'
),
'product_id_2' => array(
'Women',
'Lenses',
'Casual',
'Gucci',
),
'product_id_3' => array(
'Men',
'Sunglasses',
'Casual',
'Prada'
)
);
The idea is to recreate the keys depending on the parent category, after that we sort them using ksort():
function tree($array){
$newArray = array();
foreach ($array as $arr) {
foreach ($arr as $key => $row) {
if ($key > 0) {
$index = array();
for ($i = 0; $i <= $key; $i++)
$index[] = $arr[$i];
$index = implode('_', $index);
} else
$index = $row;
$newArray[$index] = $row;
}
}
ksort($newArray);
return $newArray;
}
Then display the HTML :
$products = tree($array);
$i = 0;
echo '<ul style="list-style-type:none">';
foreach ($products as $key => $row) {
if(strcmp($row, $key) == 0 && $i != 0)
echo '</ul><br><ul style="list-style-type:none">';
++$i;
$level = count(explode('_', $key));
$padding = 15 * (--$level);
echo
'<li style="padding-left:' . $padding . 'px">
<span style="border-left:1px dashed black;border-bottom:1px dashed black;"> ' . $row . '</span>
</li>';
}
echo '</ul>';
PHP has powerful array features: string-indexed arrays can help provide solutions to problems like this one.
For the array conversion step:
$hrchy=array();
foreach($products AS $product){//$products is as per your first array, at start…
hrchy_ins($hrchy,$product);
}
function hrchy_ins(array &$hierarchy,array $product){//$hierarchy should be passed by reference…
if(\count($product)>0){//Condition necessary to implement base case, avoiding infinite recursion
if(!isset($hierarchy[$product[0]])){$hierarchy[$product[0]]=array();}//Conditional execution ignores duplicates…
if(\count($product)>1){hrchy_ins($hierarchy[$product[0]],\array_slice($product,1));}//Condition may not be strictly necessary (see condition above!)
} }
We might now use a recursive approach for a further HTML-writing step (the recursion secret sauce = a simple recursive function including a branch-on-condition for the base-case):
function prod_list(array $hierarchy){
if(\count($hierarchy)===0){
return '';
}else{
$list='';
$list.='<ul>';
foreach($hierarchy AS $cat => $children){
$list.='<li>'.$cat;
$list.=prod_list($children);//Recursive step…
$list.='</li>';
}
$list.='<ul>';
return $list;
}
}
Finally, after defining the function, we invoke it:
echo(prod_list($hrchy));
Disclaimer: I have not tested this code.
You could transform the array in a static way, as the structure you describe always has four parts;
$hierarchy = array();
foreach($products as $product_id => $product) {
list($gender, $category, $type, $brand) = $product;
$hierarchy[$gender][$category][$type][$brand][] = $product_id;
}

Split Array by Value

I'm working on a leader board that pulls the top scorers into first, second, and third place based on points. Right now I'm working with a sorted array that looks like this (but could be of infinite length with infinite point values):
$scores = Array
(
["bob"] => 20
["Jane"] => 20
["Jill"] => 15
["John"] => 10
["Jacob"] => 5
)
I imagine I could use a simple slice or chunk, but I'd like to allow for ties, and ignore any points that don't fit into the top three places, like so:
$first = Array
(
["bob"] => 20
["Jane"] => 20
)
$second = Array
(
["Jill"] => 15
)
$third = Array
(
["John"] => 10
)
Any ideas?
$arr = array(
"Jacob" => 5,
"bob" => 20,
"Jane" => 20,
"Jill" => 15,
"John" => 10,
);
arsort($arr);
$output = array();
foreach($arr as $name=>$score)
{
$output[$score][$name] = $score;
if (count($output)>3)
{
array_pop($output);
break;
}
}
$output = array_values($output);
var_dump($output);
$first will be in $output[0], $second in $output[1] and so on.. Code is limited to 3 first places.
ps: updated to deal with tie on the third place
I would do something like:
function chunk_top_n($scores, $limit)
{
arsort($scores);
$current_score = null;
$rank = array();
$n = 0;
foreach ($scores as $person => $score)
{
if ($current_score != $score)
{
if ($n++ == $limit) break;
$current_score = $score;
$rank[] = array();
$p = &$rank[$n - 1];
}
$p[$person] = $score;
}
return $rank;
}
It sorts the array, then creates numbered groups. It breaks as soon as the limit has been reached.
You can do it with less code if you use the score as the key of the array, but the benefit of the above approach is it creates the array exactly how you want it the first time through.
You could also pass $scores by reference if you don't mind the original getting sorted.
Here's my go at it:
<?php
function array_split_value($array)
{
$result = array();
$indexes = array();
foreach ($array as $key => $value)
{
if (!in_array($value, $indexes))
{
$indexes[] = $value;
$result[] = array($key => $value);
}
else
{
$index_search = array_search($value, $indexes);
$result[$index_search] = array_merge($result[$index_search], array($key => $value));
}
}
return $result;
}
$scores = Array(
'bob' => 20,
'Jane' => 20,
'Jill' => 15,
'John' => 10,
'Jacob' => 5
);
echo '<pre>';
print_r(array_split_value($scores));
echo '</pre>';
?>

Categories