Compare multidimensional array values by key in php - php

I have an array of the products as in the sample below. Products can be two, or three and more. In this example, three.
$all_products = array(
'product_1' =>array(
'price' =>'$100',
'quantity' =>'2pcs.',
'availability'=>'In Stock',
'manufacturer'=>'Apple'),
'product_2' =>array(
'price' =>'$200',
'quantity' =>'2pcs.',
'availability'=>'In Stock',
'manufacturer'=>''),
'product_3' =>array(
'price' =>'$300',
'quantity' =>'2pcs.',
'availability'=>'In Stock',
'manufacturer'=>'')
);
I need to compare values of the products by each key. To highlight rows in the compare table where price, quantity, availability, or manufacturer is different.
I have tried this function to check which values are different and return one temp array:
function compare_array($array, $key) {
$temp_array = array();
$i = 0;
$key_array = array();
foreach($array as $val) {
if (isset($val[$key])) {
if (!in_array($val[$key], $key_array)) {
$key_array[$i] = $val[$key];
$temp_array[$i] = $val;
}
}
$i++;
}
return $temp_array;
}
and then:
foreach ($all_products as $products) {
foreach ($products as $product_key => $val) {
foreach ($this->compare_array($all_products, $product_key) as $temp_value) {
if ($val != $temp_value) {
$style[$product_key] = 'style="background-color: lightblue;"';//style for highlight
}
}
}
}
Problem is when some value in array is empty. Like in this example, manufacturer.
Maybe someone has more light solution?

I need compare values of the products by each key. To highlight rows
in the compare table where price, quantity, availability, or
manufacturer is different.
If you want to highlight all products unless all of them have exactly the same price, quantity, availability, or manufacturer.
function:
function productsIdentical(array &$products) : bool
{
if (count($products) < 2) {
throw new \InvalidArgumentException("You should pass at least 2 products to compare");
}
$compare = '';
foreach ($products as $product) {
ksort($product); //to make comparison of key order insensitive
$sha = sha1(json_encode($product));
if (empty($compare)) {
$compare = $sha;
} elseif ($sha !== $compare) {
return false;
}
}
return true;
}
returns true only if all products' fields have exactly the same keys and value, otherwise it returns false
so you use it this way:
$identicalFlag = productsIdentical($all_products);
if ($identicalFlag === false) {
echo "Products are not identical:" . PHP_EOL;
$nonIdenticalProductsArr = array_keys($all_products);
echo "Non identical products are:" . PHP_EOL;
print_r($nonIdenticalProductsArr);
//do your styling on $nonIdenticalProducts
} else {
echo "Products are identical" . PHP_EOL;
}
Output:
for identical products:
Products are identical
for non identical:
Products are not identical:
Non identical products are:
Array
(
[0] => product_1
[1] => product_2
[2] => product_3
)
Or if you want to detect every product field that is not the same across all products in the array use this function:
function getFieldsNonIdentical(array &$products) : array
{
if (count($products) < 2) {
throw new \InvalidArgumentException("You should pass at least 2 products to compare");
}
$compareArr = [];
$keyDifferentArr = [];
foreach ($products as $product) {
foreach($product as $key => $val) {
if (!key_exists($key, $compareArr)) {
$compareArr[$key] = $val;
} elseif ($compareArr[$key] !== $val) {
$keyDifferentArr[$key] = true;
}
}
}
return array_keys($keyDifferentArr);
}
this way:
$fieldsNonIdentical = getFieldsNonIdentical($all_products);
if (!empty($fieldsNonIdentical)) {
echo "Fields that are non identical:" . PHP_EOL;
print_r($fieldsNonIdentical);
//do your styling
$nonIdenticalStyle = 'style="background-color: lightblue;"';
$styleArr = [];
foreach ($fieldsNonIdentical as $key => $value) {
$styleArr[$value] = $nonIdenticalStyle;
}
echo "Non Identical fields styling is:" . PHP_EOL;
print_r($styleArr);
} else {
echo "All fields in all products are the same." . PHP_EOL;
}
Output
for identical:
All fields in all products are the same.
for non identical:
Fields that are non identical:
Array
(
[0] => price
[1] => manufacturer
)
Non Identical fields styling is:
Array
(
[price] => style="background-color: lightblue;"
[manufacturer] => style="background-color: lightblue;"
)

Related

Rank array values with possible duplicate values

I am trying to rank an array of values and maintain the order in the original array.
So for example:
(6,4,7,12,7) should return (2,1,3,4,3)
(12,17,5,27,5) should return (2,3,1,4,1)
(1,1,4,6) should return (1,1,2,3)
In each case the returned array has the rank of the corresponding element in the original array, and duplicate values will have the same rank.
$values = array(12,17,5,27,5);
$sorted_values = $values;
sort($sorted_values);
foreach ($values as $key => $value) {
foreach ($sorted_values as $sorted_key => $sorted_value) {
if ($value == $sorted_value) {
$rank_key = $sorted_key;
break;
}
}
echo $value . ' has rank: ' . $rank_key + 1 . '<br>';
}
A simple way would be to first rank the unique values (using array_unique() followed by sort()), then translate the original list by this newly rank list (more comments in the code)...
$source = [12,17,5,27,5];
$output = [];
// Copy to work array unique values
$rank = array_unique($source);
// Sort it to produce ranking order
sort($rank);
// Make the number the key with the order as the value
$rank = array_flip($rank);
// Translate the values using $rank
foreach ( $source as $element ) {
$output[] = $rank[$element]+1;
}
print_r($output);
gives...
Array
(
[0] => 2
[1] => 3
[2] => 1
[3] => 4
[4] => 1
)
Clone your array, sort the clone by values, while keeping the key associations.
Loop over the clone, and in classical “control break” fashion, compare the value of the current item to that of the previous one. Increase the rank by one, if they differ. Set that rank as new value under that key.
Sort the clone by keys.
$data = [12,17,5,27,5];
$clone = $data;
asort($clone);
$rank = 0;
$previous_value = null; // something that doesn't ever occur in your array values
foreach($clone as $key => $value) {
if($value !== $previous_value) {
$rank++;
}
$clone[$key] = $rank;
$previous_value = $value;
}
ksort($clone);
var_dump($data, $clone);
you need to sort your array
This may close to your answer:
<?php
$my_array=array(12,17,5,27,5);
$ordered_values = $my_array;
$rank=array();
rsort($ordered_values);
foreach ($ordered_values as $key => $value) {
foreach ($ordered_values as $ordered_key => $ordered_value) {
if ($value === $ordered_value) {
$key = $ordered_key;
break;
}
}
$rank[$value]=((int) $key + 1) ;
}
foreach($my_array as $key => $value)
{
echo $value.':'.$rank[$value].'<br>';
}
?>
function rankDuplicateArray(array $array): array
{
$copy = array_unique($array);
sort($copy);
$flipArray = array_flip($copy);
return array_map(function($v) use ($flipArray) {
return $flipArray[$v] + 1;
}, $array);
}

Get unique variations of multiple arrays

I have an array which has different items example shirts, shoes, ties, jackets etc. And each of these items can have multiple designs denoted by their ids.
$variations = array(
'shirts' => array('2'),
'shoes' => array('7', '3'),
'jackets' => array('1', '5')
);
Now we are looking for an efficient way to create different variations of all of these.
## Required result ##
$result = array(
array('2','7','1'),
array('2', '7', '5'),
array('2','3','1'),
array('2','3','5')
);
Any help would be appreciated :)
EDIT: Our current function
function cartesian($input) {
$result = array();
while (list($key, $values) = each($input)) {
// If a sub-array is empty, it doesn't affect the cartesian product
if (empty($values)) {
continue;
}
// Seeding the product array with the values from the first sub-array
if (empty($result)) {
foreach($values as $value) {
$result[] = array($key => $value);
}
}
else {
// Second and subsequent input sub-arrays work like this:
// 1. In each existing array inside $product, add an item with
// key == $key and value == first item in input sub-array
// 2. Then, for each remaining item in current input sub-array,
// add a copy of each existing array inside $product with
// key == $key and value == first item of input sub-array
// Store all items to be added to $product here; adding them
// inside the foreach will result in an infinite loop
$append = array();
foreach($result as &$product) {
// Do step 1 above. array_shift is not the most efficient, but
// it allows us to iterate over the rest of the items with a
// simple foreach, making the code short and easy to read.
$product[$key] = array_shift($values);
// $product is by reference (that's why the key we added above
// will appear in the end result), so make a copy of it here
$copy = $product;
// Do step 2 above.
foreach($values as $item) {
$copy[$key] = $item;
$append[] = $copy;
}
// Undo the side effecst of array_shift
array_unshift($values, $product[$key]);
}
// Out of the foreach, we can add to $results now
$result = array_merge($result, $append);
}
}
return $result;
}
While I agree with comments beneath your question I implemented generator-based solution for fun so I can share it anyway:
$variations = array(
'shirts' => array('2'),
'shoes' => array('7', '3'),
'jackets' => array('1', '5')
);
var_dump(iterator_to_array(cartesian($variations), false));
function cartesian($lists, $product = [])
{
if (empty($product)) {
// first run, reverse array for array_pop and remove empty lists
$lists = array_reverse(array_filter($lists, 'count'));
}
$curr = array_pop($lists);
foreach ($curr as $c) {
if (empty($lists)) {
yield array_merge($product, [$c]);
} else {
yield from cartesian($lists, array_merge($product, [$c]));
}
}
}
This is how I did it
$parameters = array(
'shirts' => array('2'),
'shoes' => array('7', '3'),
'jackets' => array('1', '5')
);
$arPhrases = $parameters[0];
for ($i = 1; $i < count($parameters); $i++) {
$notFullCount = count($arPhrases);
foreach ($arPhrases as $phrase) {
foreach ($parameters[$i] as $newPart) {
$arPhrases[] = $phrase." ".$newPart;
}
}
$arPhrases = array_slice($arPhrases, $notFullCount);
}

using array_key_exists to pair up the amount total to id

I have an array that has several amounts (based on $$$ sales) attached to an id (where some of the ids are the same based on whom made a sale). My goal is to gather a total of amounts and attach each total to whichever id made the sale (truncating the identical ids from the array). I'm honestly not sure how to go about this as I'm still learning.
while ($row) {
$operearnedArray[] = array(
'amount' => $row['ChargeAmount'],
'id' => $row['OperatorID']);
}
//$operearnedArray returns the array with each "amount" attached to "id"
foreach ($operearnedArray as $key => $value) {
if($value['id'] == '' || $value['id'] == null) {
continue;
}
if(array_key_exists($value['id'], $operSums)) {
$operSums[$value['id']] += $value['amount'];
} else {
//I'm not sure where to go from here...
}
}
Your code looks fine to me, as for the comment, a simple
$operSums[$value['id']] = $value['amount'];
should do the trick. Of course it is adviseable to check for existent keys, but
foreach ($operearnedArray as $key => $value) {
if($value['id'] == '' || $value['id'] == null) {
continue;
}
$operSums[$value['id']] += $value['amount'];
}
should work just as well, as you can see in this demo.
try:
while ($row) {
$operearnedArray[] = array(
'amount' => $row['ChargeAmount'],
'id' => $row['OperatorID']);
}
$operSums = array();
foreach ( $operearnedArray as $sale ) {
if ( ! $sale['id'] ) {
continue;
}
$operSums[$sale['id']] += $sale['amount'];
}
// do something with the result, let's just look at it:
var_dump( $operSums );
It works: http://codepad.org/xGMNQauW

extract duplicates and how many times they occur in php array

I am developing a user driven eCommerce website and need some help. What I have is a function that will loop through an array remove duplicates and how many times they occur. I then need to run a function on each of those extracted duplicates as many times as they occur. The code I have so far works, but breaks when there are multiple duplicates with the same repetition count. Here is the code I have made so far..
$affiliates = array(11,11,12,12,13,13,13,14,14,14,14); //breaks the code
$affiliates = array(11,11,13,13,13,14,14,14,14,12,12,12,12,12); // works fine
$array = array();
$match_count = array();
foreach($affiliates as $key => $affiliate) {
$array[] = $affiliate;
}
arsort($array); // keeps array index in order
foreach($array as $arrays) {
if(array_value_count($arrays,$array) > 1) {
$match_count[] = array_value_count($arrays,$array);
}
}
$match_count = array_unique($match_count);
$array_unique = arrayDuplicate($array);
$final_array = array_combine($match_count,$array_unique);
foreach($final_array as $key => $value) {
for($i = 0; $i < $key; $i++) {
echo 'addOrder(affiliate_id = ' . $value . ') . '<br>';
}
}
the functions
function unique_array($array) {
return array_unique($array, SORT_NUMERIC);
}
function arrayDuplicate($array) {
return array_unique(array_diff_assoc($array,array_unique($array)));
}
function array_value_count($match, $array) {
$count = 0;
foreach ($array as $key => $value)
{
if ($value == $match)
{
$count++;
}
}
return $count;
}
to fix the duplicates breaking the code I have tried this
if(count($array_unique) - count($match_count_unique) == 1 ) // do something
or
if(count($array_unique) != count($match_count_unique) == 1 ) // do something
How would I know where to add the missing duplicate value count and array items correctly without them getting out of sync? OR Is there a better way of doing this?
Taken from How do I count occurrence of duplicate items in array
$array = array(12,43,66,21,56,43,43,78,78,100,43,43,43,21);
$vals = array_count_values($array);
echo 'No. of NON Duplicate Items: '.count($vals).'<br><br>';
print_r($vals);
Result
No. of NON Duplicate Items: 7
Array
(
[12] => 1
[43] => 6
[66] => 1
[21] => 2
[56] => 1
[78] => 2
[100] => 1
)
Duplicate items = (Array Size) - (Total Number of Unique Values)
<?php
$affiliates = array(11,11,12,12,13,13,13,14,14,14,14);
// get an array whose keys are the aff# and
//the values are how many times they occur
$dupes = array();
foreach ($affiliates as $aff) {
$dupes[$aff]++;
}
// remove the 1's since those aren't dupes
$dupes = preg_grep('~^1$~',$dupes,PREG_GREP_INVERT);
// de-dupe the original array
$affiliates = array_unique($affiliates);
// for each duped affiliate...
foreach ($dupes as $aff => $affCount) {
// for each time it was duped..
for ($c=0;$c<$affCount;$c++) {
// do something. $aff is the aff# like 11
}
}
?>

ordering categories with php arrays

I have an array of categories:
categories = computers, entertainment, products, graphics cards
The array is sometimes returned in the wrong order BUT each category has a parent which exists in the SAME array.
categories =
products[parent=0],
entertainment[parent=products],
computers[parent=entertainment],
graphics cards[parent=computers]
How would I use php to sort this array if it was returned in any order?
Unordered Example:
categories =
computers[parent=entertainment],
entertainment[parent=products],
products[parent=0],
graphics cards[parent=computers]
Must produce:
categories = products, entertainment, computers, graphics cards
Are you talking about a simple sort like this:
<?php
$categories = array('computers[parent=entertainment]',
'entertainment[parent=products]',
'products[parent=0]',
'graphics cards[parent=computers]');
sort($categories);
echo '<pre>';
print_r($categories);
echo '</pre>';
?>
Looking at your example, I'm assuming you want a topological sort, as Sam Dufel says.
$categories = array(
"computers" => array("parent" => "entertainment"),
"entertainment" => array("parent" => "products"),
"products" => array("parent" => null),
"graphics cards" => array("parent" => "computers")
);
// Set distances
foreach ($categories as $cat => &$e) {
$e["dist"] = nodeDistance($cat, $categories);
}
// Before
echo implode(", ", array_keys($categories)) . "\n";
// Sort
uasort($categories, "nodeDistanceSorter");
// After
echo implode(", ", array_keys($categories)) . "\n";
function nodeDistance($node, $categories) {
// Check cache
if (array_key_exists("dist", $categories[$node]))
return $categories[$node]["dist"];
// Check root
if (is_null($categories[$node]["parent"]))
return 0;
// Traverse
return nodeDistance($categories[$node]["parent"], $categories) + 1;
}
function nodeDistanceSorter($a, $b) {
$aDist = $a["dist"];
$bDist = $b["dist"];
if ($aDist == $bDist)
return 0;
return $aDist - $bDist;
}
categories =
products[parent=0],
entertainment[parent=products],
computers[parent=entertainment],
graphics cards[parent=computers]
I take it that the array is like this:
$categories = array
(
'products'=>array('parent'=>0),//poor little orphan
'entertainment'=>array('parent'=>'products'),
'computers'=>array('parent'=>'entertainment'),
'graphics cards'=>array('parent'=>'computers'),
)
here is a solution that I mixed up right now:
Solution 1
$categories = array
(
'computers'=>array('parent'=>'entertainment'),
'entertainment'=>array('parent'=>'products'),
'products'=>array('parent'=>0),
'graphics cards'=>array('parent'=>'computers')
);
function addparentfirst(&$original,$array,$name,$data){
if(isset($data['parent']) && isset($original[$data['parent']])){
$array = addparentfirst($original,$array,$data['parent'],$original[$data['parent']]);
}
$array[$name] = $data;
unset($original[$name]);
return $array;
}
foreach($categories as $key=>$value){//goes over each category only once, contrary to what it looks like
$sortedcategories = addparentfirst($categories,$sortedcategories,$key,$value);
}
$categories = $sortedcategories;//NOW IT'S SORTED
print_r($categories);
Solution 2
//It's interesting that it doesn't loop infinitely
//I like this solution the most
function addparentfirst(&$array,$key){
if(isset($array[$key]['parent']) && !empty($array[$key]['parent'])){
addparentfirst($array,$array[$key]['parent']);
}
$data = $array[$key];
unset($array[$key]);
$array[$key] = $data;
return $array;
}
foreach($categories as $key=>$value){
addparentfirst($categories,$key);
}
print_r($categories);
Solution 3
function sortArrayByArray($array,$orderArray) {
$ordered = array();
foreach($orderArray as $key) {
if(isset($array[$key])) {
$ordered[$key] = $array[$key];
unset($array[$key]);
}
}
return $ordered + $array;
}
//Usage:
$categories = sortArrayByArray($categories,array('products','entertainment','computers','graphics cards'));
print_r($categories);
as seen here
Solution 4
function get_childless_category_name($array){
foreach($array as $key=>$value){
if(isset($value['parent']) && !empty($value['parent'])){
unset($array[$value['parent']]);
}
}
$names = array_keys($array);//names of all the child free categories
return array_pop($names);//get the last one (there should only be one)
}
function parent_comes_first(&$array,$key){
if(isset($array[$key]['parent']) && !empty($array[$key]['parent'])){
$array = parent_comes_first($array,$array[$key]['parent']);
}
$data = $array[$key];
unset($array[$key]);
$array[$key] = $data;
return $array;
}
//Usage:
$childless = get_childless_category_name($categories);
parent_comes_first($categories,$childless);
print_r($categories);

Categories