Comparing & Counting Matched Values of Two Associative Arrays - PHP - php

I have been researching comparing two associative arrays, which I have only been able to do with a fair degree of accuracy. I have read all similar threads on S.O. but none have solved or addressed the issue I am having, which is, while comparing two associative arrays the test data will successfully show the appropriate matches, however when I attempt to count the number of matched values, I am getting some strange results.
EDIT:
<?php
$data = array(
'Alpha' => array(
'peace' => 0,
'art' => 1,
'trend' => 0,
'night' => 1,
'shop' => 0
),
'Beta' => array(
'peace' => 1,
'art' => 1,
'trend' => 1,
'night' => 1,
'shop' => 0
),
'Gamma' => array(
'peace' => 0,
'art' => 1,
'trend' => 1,
'night' => 1,
'shop' => 0
)
);
$choices = array(
'peace' => 0,
'art' => 1,
'trend' => 0,
'night' => 1,
'shop' => 0
);
function compare($data, $choices)
{
foreach ($data as $city => $name)
{
echo $city . '<br>';
foreach ($name as $key => $value)
{
($choices[$key] === $value) ? $match = 'match' : $match = 'no';
($choices[$key] === $value) ? $i++ : $i = 0;
echo $key . ':' . $value . ':' . $choices[$key] . ':' . $match . '<br>';
}
echo 'Matches:' . $i . '<br><br>';
}
}
compare($data, $choices);
?>
OUTPUT DATA
Format of data is as follows
-----------------------------
name of key:$data value:$choices value:is match
Alpha
peace:0:0:match
art:1:1:match
trend:0:0:match
night:1:1:match
shop:0:0:match
Matches:5
Beta
peace:1:0:no
art:1:1:match
trend:1:0:no
night:1:1:match
shop:0:0:match
Matches:2
Gamma
peace:0:0:match
art:1:1:match
trend:1:0:no
night:1:1:match
shop:0:0:match
Matches:2
'Alpha' should return 5 matches, which it does.
'Beta' should return 3, it returns 2.
'Gamma' should return 4, it returns 2.
Any help would be greatly appreciated. Thank you in advance.

The problem is how you are incrementing the count with a ternary statement. When you do
($choices[$key] === $value) ? $i++ : $i = 0;
It will reset $i to zero any time it encounters a non-match.
Using a simple conditional instead should give you the correct count.
if ($choices[$key] === $value) $i++;
You can initialize $i to 0 before the inner foreach loop.

Related

how to sum in a multidimensional array in php

As an example I have an array like this:
$Batches = array(
$Batch_1= array(
$A_Row= array(1,1,0,0),
$B_Row = array(0,0,0,1),
$C_Row = array(0,0,0,1)),
$Batch_2= array(
$A_Row= array(1,0,0,0),
$B_Row = array(0,0,0,1),
$C_Row = array(0,0,0,1))
);
I want to sum per each $n_Row:
So I should get a result like this:
$Sum_Array = array(
$A_row=array(2,1,0,0),
$B_row= array(0,0,0,2),
$C_row = array(0,0,0,2)
);
But I'm having difficulty trying to get this result and need some tips/advice from someone else.
This is my code so far:
//count rows and columns
$count_array= count($Batches);
$count_row = count($Batches[0]);
$count_column = count($Batches[0][0]);
$Array_sum = array();
for($array=0;$array<$count_array;$array++){
for($row=0;$row<$count_row;$row++){
for($col=0;$col<$count_column;$col++){
//echo $Batches[$array][$row][$col] . "\n";
if($Batches[$array][$row][$col]==1){
$Batches[$array][$row][$col]++;
}
$Array_sum[]=$Batches[$array][$row][$col];
}
}
}
Anyone who can help me in the right direction?
Thanks in advance.
I think your "array"
$Batches = array(
$Batch_1= array(
$A_Row= array(1,1,0,0),
$B_Row = array(0,0,0,1),
$C_Row = array(0,0,0,1)),
$Batch_2= array(
$A_Row= array(1,0,0,0),
$B_Row = array(0,0,0,1),
$C_Row = array(0,0,0,1))
);
is a product of the imagination. But it is also valid PHP code. I assume you want to work with keys and the array looks like this:
$Batches = array(
'Batch_1' => array(
'A_Row' => array(1,1,0,0),
'B_Row' => array(0,0,0,1),
'C_Row' => array(0,0,0,1)),
'Batch_2' => array(
'A_Row' => array(1,0,0,0),
'B_Row' => array(0,0,0,1),
'C_Row' => array(0,0,0,1))
);
The Summation works better with foreach.
$sum_arr = [];
foreach($Batches as $ib => $batch){
foreach($batch as $ir => $row){
foreach($row as $ic => $col){
$sum_arr[$ir][$ic] ??= 0;
$sum_arr[$ir][$ic] += $col;
}
}
}
var_export($sum_arr);
The Output:
array (
'A_Row' =>
array (
0 => 2,
1 => 1,
2 => 0,
3 => 0,
),
'B_Row' =>
array (
0 => 0,
1 => 0,
2 => 0,
3 => 2,
),
'C_Row' =>
array (
0 => 0,
1 => 0,
2 => 0,
3 => 2,
),
)
The summation also works with the original array. However, this array has only numeric keys.

In search of a recursive function to replace all * with nested levels in combination with dotnotation of Laravel

For our project we're creating a mapper. We are doing this in combination with the Laravel dot notation helper. I am stuck on this for a moment now and I tried a lot of different recursive functions but none are giving me the expected result. It has to translate between a dotnotated array A to dotnotated array B.
use Illuminate\Support\Arr;
Class ...
private function strpos_n($haystack, $needle, $nth = null) {
$offset = 0;
$allPositions = array();
while (($pos = strpos($haystack, $needle, $offset)) !== FALSE) {
$offset = $pos + 1;
$allPositions[] = $pos;
}
return $nth === null ? $allPositions : ($allPositions[$nth] ?? '');
}
private function str_replace_n($search, $replace, $subject, $nth) {
$found = preg_match_all('/'.preg_quote($search).'/', $subject, $matches, PREG_OFFSET_CAPTURE);
if (false !== $found && $found > $nth) {
return substr_replace($subject, $replace, $matches[0][$nth][1], strlen($search));
}
return $subject;
}
...
$data = Arr::dot([
'items' => [
[
'type' => 'product',
'number' => 'TST_001',
'quantity' => 5,
'price' => 1.25,
'variant' => [
[
'color' => 'Navy Blue'
],
[
'color' => 'Bright Red'
]
]
],
[
'type' => 'product',
'number' => 'TST_002',
'quantity' => 2,
'price' => 8.34,
'variant' => [
[
'color' => 'Shady White'
],
[
'color' => 'Dark Orange'
],
[
'color' => 'Lemon Yellow'
]
]
]
]
]); // Hardcoded for testing
$map = [
'items.*.type' => 'lines.*.type',
'items.*.number' => 'lines.*.sku',
'items.*.quantity' => 'lines.*.quantity',
'items.*.price' => 'lines.*.price',
'items.*.variant.*.color' => 'lines.*.colors.*.color',
]; // Hardcoded for testing
The function I'm searching for needs to search if the key exists in the data array, save as the mapper key and then 'level up' whenever the key doesn't exist go to the next entry and repeat (also for sub-levels).
What I got so far:
foreach($map as $from => $to) {
$hits = [];
// Count occurrences
$occurrences = substr_count($from, '*');
// For each occurrence
for($nth = 0; $nth < $occurrences; $nth++) {
// Always start with 0 levels
$fromKey = $from;
$toKey = $to;
// Save when key exists
if(isset($data[$fromKey])) {
$hits[$toKey] = $data[$fromKey];
}
// For each nested
for($i = $nth; $i >= 0; $i--) {
$level = 0;
$fromKey = $this->str_replace_n(($level == 0 ? '*' : $level - 1).'.', $level . '.', $fromKey, $i);
$toKey = $this->str_replace_n(($level == 0 ? '*' : $level - 1).'.', $level . '.', $toKey, $i);
// Save when key exists
while(isset($data[$fromKey])) {
$hits[$toKey] = $data[$fromKey];
$level++;
$fromKey = $this->str_replace_n(($level - 1).'.', $level . '.', $fromKey, $i);
$toKey = $this->str_replace_n(($level - 1).'.', $level . '.', $toKey, $i);
}
}
}
}
Which produces the following output:
{
"lines.0.type": "product",
"lines.1.type": "product",
"lines.0.sku": "TST_001",
"lines.1.sku": "TST_002",
"lines.0.quantity": 5,
"lines.1.quantity": 2,
"lines.0.price": 1.25,
"lines.1.price": 8.3399999999999999,
"lines.0.colors.0.color": "Navy Blue",
"lines.1.colors.0.color": "Shady White"
}
While I'm expecting:
{
"lines.0.type": "product",
"lines.1.type": "product",
"lines.0.sku": "TST_001",
"lines.1.sku": "TST_002",
"lines.0.quantity": 5,
"lines.1.quantity": 2,
"lines.0.price": 1.25,
"lines.1.price": 8.3399999999999999,
"lines.0.colors.0.color": "Navy Blue",
"lines.0.colors.1.color": "Bright Red", // sublevel missing
"lines.1.colors.0.color": "Shady White",
"lines.1.colors.1.color": "Dark Orange", // sublevel missing
"lines.1.colors.2.color": "Lemon Yellow" // sublevel missing
}
When providing a solution, please keep in mind that it has to be dynamic. Input/outputs do not always consist of only 1 sublevel, it could be infinite.
I've searched for any existing script, but couldn't find it only. Maybe I just don't know how this is called and am unable to find it, while it already exists for Laravel (out of the box)? Any help is appreciated!
UPDATE
For imports the order of the output array keys aren't any issue, since you search on the key. But for exporting data back to a customer it should be in the order as it was provided.
So: when the given data array (keys) is in a different order than the mapping array (keys). The output should be based on the mapping array (keys) order. For example if we 'shift' the data array into:
$data = Arr::dot([
'items' => [
[
'variant' => [
[
'color' => 'Navy Blue'
],
[
'color' => 'Bright Red'
]
],
'quantity' => 5,
'type' => 'product',
'number' => 'TST_001',
'price' => 1.25
],
[
'number' => 'TST_002',
'quantity' => 2,
'variant' => [
[
'color' => 'Shady White'
],
[
'color' => 'Dark Orange'
],
[
'color' => 'Lemon Yellow'
]
],
'price' => 8.34,
'type' => 'product'
]
]
]);
And keep the mapping as is. The output should still be:
Array
(
[lines.0.type] => product
[lines.0.sku] => TST_001
[lines.0.quantity] => 5
[lines.0.price] => 1.25
[lines.0.colors.0.color] => Navy Blue
[lines.0.colors.1.color] => Bright Red
[lines.1.type] => product
[lines.1.sku] => TST_002
[lines.1.quantity] => 2
[lines.1.price] => 8.34
[lines.1.colors.0.color] => Shady White
[lines.1.colors.1.color] => Dark Orange
[lines.1.colors.2.color] => Lemon Yellow
)
First, we recursively flatten the array and then map to the standard result for simplicity and clean code.
Step 1:
To recursively generate the result, we first loop through each key and value pair and assign the dot notations accordingly.(Ignore this if Arr::dot() did this already and I did this since I have never used Laravel helpers so much).
Snippet:
<?php
$result = [];
function recursiveFlattening($data,$parent_key,&$result){
foreach($data as $key => $value){
if(is_array($value)){
recursiveFlattening($value,trim($parent_key . "." . $key,"."),$result);
}else{
$result[trim($parent_key . "." . $key,".")] = $value;
}
}
}
recursiveFlattening($data,'',$result);
Step 2:
We need to map our flattened result with $map as you already defined.
In the below code, we preg_match() the flattened key with the pattern in $map.
If they both match, we explode() based on . and assign every bit (except the missing bit *, as it's already assigned in the key) with it's corresponding value and assign it as a new key to the final result.
Pre-requisite:
It is always good to arrange the mapping pattern in descending order, matching the longest first and then moving to smaller patterns if not matched. Since we are doing a regex match, .* could perform a greedy match and override longer rules which had smaller rules as a prefix.
Colliding patterns example(while regex matching):
'items.*.variant.*.color' => 'lines.*.colors.*.color'
'items.*.variant.*.color.*.color' => 'lines.*.colors.*.color.*.false_positive_case'
Hence, we uksort() them in descending order to make sure we match correctly.
Pattern arrangement Snippet:
uksort($map,function($pattern_1,$pattern_2){
return count(explode(".",$pattern_2)) - count(explode(".",$pattern_1));
});
Mapping Snippet:
<?php
function mapToStandardFormat($result,$map){
$mapped_result = [];
foreach($result as $key => $value){
$exploded_original_key = explode(".",$key);
foreach($map as $pattern => $destination_pattern){
if(preg_match("/^" . $pattern . "$/",$key) === 1){
foreach(explode(".",$destination_pattern) as $pat_key => $pat_value){
if($pat_value !== '*'){
$exploded_original_key[$pat_key] = $pat_value;
}
}
$mapped_result[implode(".",$exploded_original_key)] = $value;
break; // break because it matched with current pattern itself
}
}
}
return $mapped_result;
}
print_r(mapToStandardFormat($result,$map));
Full demo code: https://3v4l.org/gunO5

Laravel : How to count specific array values

I have been trubling with session array part...as my session array have 3 different value coming from database ...easy ...medium ...hard....how i count these specificly?
Session::push('getscoresession',$getscore);
Session::push('level',$level);
$getsession = [ 'qid' => $getidvalue, 'answer' => $getanswervalue];
Session::push('answer', $getsession);
$score = array_count_values(Session::get("level"));
return view('score',compact('score'));
getting this error message: array_count_values(): Can only count STRING and INTEGER values!`
Here is your solution:
$array = [
0 => 'easy',
1 => 'easy',
2 => 'easy',
3 => 'medium',
4 => 'medium',
5 => 'medium',
6 => 'hard',
7 => 'hard',
8 => 'hard',
9 => 'hard',
10 => 'hard'
];
echo "<pre>";
print_r($array);
var_dump(array_count_values($array));
Another solution:
// This is static
$stats = [
'easy' => 0,
'medium' => 0,
'hard' => 0,
];
// Alternatively dynamic way:
$a = array_flip(array_unique($array));
$b = array_fill_keys(array_keys($a), 0);
foreach($array as $value) {
$stats[$value]++; // Static Way
$b[$value]++; // Dynamic way
}
echo "<pre>";
print_r($stats);
print_r($b);
exit;
You can use http://www.writephponline.com/ to execute above code.
In your blad file:
Use foreach
#foreach($data as $key => $value)
{{ $key .'-'. $value }}
#endforeach
Let me know if you still have any query.
It's may be because you may have null values in your array.
Possible solutions :
Either remove null values from your array.
Or use array_filter.
See this example:
https://repl.it/repls/TameFarLine

How can I achieve this in Array in PHP

am having problem achieving this result with array, I want to update another empty array with data in first array in such a way that the first two rows is ommitted and the first 3rows is added to the index 0 of the empty array, and the next 3 rows is also updated to the second index and so on. I have this Array in
`$arr = [
'tqty' => 9,
'tPrice' => 18700,
'food_name_1' => 'Black Coffee',
'food_quanty_1' => 1,
'food_price_1' => 1000,
'food_name_2' => 'Sub Combo',
'food_quanty_2' => 2,
'food_price_2' => 3000
];`
I want to use this array data and update another empty array this way, removing the first two rows
$arr2 = [
0 => [
'food_name_1' => 'Black Coffee',
'food_quanty_1' => 1,
'food_price_1' => 1000
],
1 => [
'food_name_2' => 'Sub Combo',
'food_quanty_2' => 2,
'food_price_2' => 3000
]
];
here is my code so far
$arr = [
'tqty' => 9,
'tPrice' => 18700,
'food_name_1' => 'Black Coffee',
'food_quanty_1' => 1,
'food_price_1' => 1000,
'food_name_2' => 'Sub Combo',
'food_quanty_2' => 2,
'food_price_2' => 3000
];
$newdat = [];
$count = 0;
$oldcount = 1;
foreach($arr as $key => $value){
if(preg_match_all('!\d+!', $key, $matches)){
if($oldcount == $matches[0][0]){
$newdat[$matches[0][0]] = [
$count => [
$key => $value
]
];
} else{
$count = 0;
$oldcount = $matches[0][0];
}
}
$count++;
}
I hope I get help soon. thanks
Assuming the array keys and order stay consistent you could use array_chunk
<?php
$inArray = [
'tqty' => 9,
'tPrice' => 18700,
'food_name_1' => 'Black Coffee',
'food_quanty_1' => 1,
'food_price_1' => 1000,
'food_name_2' => 'Sub Combo',
'food_quanty_2' => 2,
'food_price_2' => 3000,
];
$outArray = [];
// Remove first 2 values.
$inArray = array_slice( $inArray, 2 );
// 'Chunk' the rest of the values.
// true preserves keys.
$outArray = array_chunk( $inArray, 3, true );
echo '<pre>' . print_r( $outArray, true ) . '</pre>';
/**
Output:
<pre>Array
(
[0] => Array
(
[food_name_1] => Black Coffee
[food_quanty_1] => 1
[food_price_1] => 1000
)
[1] => Array
(
[food_name_2] => Sub Combo
[food_quanty_2] => 2
[food_price_2] => 3000
)
)
</pre>
*/
If I have understood you correctly,
$newdata = array();
for ($i = 1; $i++;) { // Intentionally no condition set.
if (array_key_exists('food_name_' . $i, $arr)) {
$temparray = array();
$temparray['food_name_' . $i] = $arr['food_name_' . $i];
if (array_key_exists('food_quanty_' . $i, $arr)) {
$temparray['food_quanty_' . $i] = $arr['food_quanty_' . $i];
}
if (array_key_exists('food_price_' . $i, $arr)) {
$temparray['food_price_' . $i] = $arr['food_price_' . $i];
}
$newdata[] = $temparray;
} else {
break; // break out of the loop
}
}
echo "<pre>";
print_r($newdata);
echo "</pre>";
die();
Loop through it and build the array index string via variables.
<?php
$arr = [
'tqty' => 9,
'tPrice' => 18700,
'food_name_1' => 'Black Coffee',
'food_quanty_1' => 1,
'food_price_1' => 1000,
'food_name_2' => 'Sub Combo',
'food_quanty_2' => 2,
'food_price_2' => 3000
];
foreach(array("food_name_","food_quanty_","food_price_") as $v){
// replace the set value of 2 here with a count() on the $arr
// and some basic math - IF you are always sure you'll have 3 fields
for($i=0;$i<2;$i++){
$newarr[$i][$v.($i+1)]=$arr[$v.($i+1)];
}
}
print_r($newarr);
?>
Here's a solution that will locate the _x at the end to check its a digit
This solution does not worry about how many non numbered fields you have, or how many fields there are per numbered "row", they are also indexed based on the _x number.
$arr = [
'tqty' => 9,
'tPrice' => 18700,
'food_name_1' => 'Black Coffee',
'food_quanty_1' => 1,
'food_price_1' => 1000,
'food_name_2' => 'Sub Combo',
'food_quanty_2' => 2,
'food_price_2' => 3000
];
$arr2 = array();
foreach( $arr as $key => $value )
{
$explode = explode( '_', $key );
if( ctype_digit( $index = end( $explode ) ) === true)
{
if( isset( $arr2[ $index ] ) === false )
{
$arr2[ $index ] = array();
}
$arr2[ $index ][ substr( $key, 0, strlen( $key ) - 1 - strlen( $index ) ) ] = $value;
}
}
print_r( $arr2 );
Output:
Array
(
[1] => Array
(
[food_name] => Black Coffee
[food_quanty] => 1
[food_price] => 1000
)
[2] => Array
(
[food_name] => Sub Combo
[food_quanty] => 2
[food_price] => 3000
)
)
If you already know that you need always the same indexes from $arr, you can use the array_keys function in order to index the associative array.
Example:
$keys = array_keys($arr);
echo '<br><br>'.$arr[$keys[1]];
Here is a complete example:
$keys = array_keys($arr); #stores the associative keys by index
$arr2 = array();
/* For each array $arr do the following */
$limit = 5; #substitute this with: count($arraylist)
for( $n_array=0; $n_array<limit; $n_array++ ){
$cur_arr = array(); #substitute with your: $arraylist[ $n_array ]
for( $a = 2; $a<count($arr); $a++ ){
$cur_arr[ $keys[$a] ] = $arr[ $keys[$a] ];
}
$arr2[ $n_array ] = $cur_arr;
}
Hope it will be helpful

array_search always gives me false

So i am trying to search an array and it always gives me false for some reason even tho the letter exists in the name array. This is what i want it to do , search the array, if a match exist it should add the name to the $data array and send it to the AJAX by encode it to JSON. But my IF always gives false
Edit: The answer in the other question did not solve this since that will always cause my array to return false
$user = array(
0 => array(
"id"=> 0,
"photo"=>"smily",
"name"=>"Name One",
),
1 => array(
"id"=> 0,
"photo"=>"smily",
"name"=>"Name Five",
),
2 => array(
"id"=> 0,
"photo"=>"smily",
"name"=>"Name Four",
),
3 => array(
"id"=> 0,
"photo"=>"smily",
"name"=>"Name Three",
),
);
$wordToSearch = "N";
$data = Array();
for($i = 0; $i < count($user); $i++)
{
if(array_search($wordToSearch, array_column($user, 'name')) == false)
{
array_push($data, array(
'name' => $user[$i]['name'],
'img' => $user[$i]['photo'],
'pos' => $user[$i]['position'],
'Id' => $user[$i]['id']
)
);
echo json_encode($data);
}
}
I could be wrong, but instead of
if(array_search($wordToSearch, array_column($user, 'name')) == false)
I think you want
if (strpos($user[$i]['name'], $wordToSearch) !== false)
array_column is used to get all the user names or all the user ids
array_search is used to search an array... but I think you're trying to search a string.
unrelated... instead of using array_push($data, array(...));, use $data[] = array(...); it's a bit more efficient as there's no function call.. and in my opinion, it's easier to code & read.
You can use strpbrk - strpbrk — Search a string for any of a set of characters
foreach ($user as $val) {
if (strpbrk($val['name'], $wordToSearch) != false) {
array_push($data, array(
'name' => $val['name'],
'img' => $val['photo'],
'pos' => $val['position'],
'Id' => $val['id']
)
);
echo json_encode($data);
}
}
}

Categories