How to show duplicate data in multidimensional array using PHP - php

I am using Spout Excel reader to read Excel files from php code and saving into a multidimensional array in PHP variable,Array looks like this
$array = [
[
'id[0]' => 'BX-78',
'Name[0]' => 'XXX',
'Address[0]' => 'YUUSATD'
],
[
'id[1]' => 'BX-79',
'Name[1]' => 'YYY',
'Address[1]' => 'DHJSHDJGY'
],
[
'id[2]' => 'BX-80',
'Name[2]' => 'ZZZ',
'Address[2]' => 'DDSDSDA'
]
[
'id[3]' => 'BX-78',
'Name[3]' => 'AAA',
'Address[3]' => 'FSDSDS'
][
'id[4]' => 'BX-81',
'Name[4]' => 'XXX',
'Address[4]' => 'DSDSDSD'
]];
Now i want to show duplicate data from above array using two keys ['id'] and ['name'] if id repeats show as duplicate data,
If name repeats show that row as duplicate data if both are duplicate show as again duplicate row
Otherwise it is unique row.
I have tried using multidimensional array sorting but it is using only one key to match data in rows.
foreach ($arrExcelData as $v) {
if (isset($arrExcelData[$v[0]])) {
// found duplicate
continue;
}
// remember unique item
$arrExcelData3[$v[0]] = $v;
}
// if you need a zero-based array, otheriwse work with $_data
$arrExcelData2 = array_values($arrExcelData3);
Edited : Expected Output Result :
Matching Rows:
Id Name Address
-------------------------
BX-78 XXX YUUSATD
BX-78 AAA DDSDSDA
BX-81 XXX DSDSDSD`

If you want to list the duplicate values, I think the address of the second match should be FSDSDS as there is not item with name AAA and value DDSDSDA:
BX-78 AAA FSDSDS
If that is the case, what you could do is to first use a double foreach to mark the arrays that contain a duplicate id or name by for example adding a property named id and name except when the array is itself in the second loop.
After this loop, you can tell which arrays are the duplicate ones. Instead of using a corresponding index 0 as in id[0], I have used reset and next so it is not tied to these indexes.
To get the filtered result you could use array_reduce to check for the array keys and unset them.
For example:
foreach ($array as $index => $a) {
foreach ($array as $v) {
if ($v === $a) continue;
if (reset($v) === reset($a)) $array[$index]["id"] = "duplicate";
if (next($v) === next($a)) $array[$index]["name"] = "duplicate";
}
}
$array = array_reduce($array, function($carry, $item) {
if (array_key_exists("id", $item) || array_key_exists("name", $item)) {
unset($item["id"], $item["name"]);
$carry[] = $item;
}
return $carry;
}, []);
print_r($array);
Result
Array
(
[0] => Array
(
[id[0]] => BX-78
[Name[0]] => XXX
[Address[0]] => YUUSATD
)
[1] => Array
(
[id[3]] => BX-78
[Name[3]] => AAA
[Address[3]] => FSDSDS
)
[2] => Array
(
[id[4]] => BX-81
[Name[4]] => XXX
[Address[4]] => DSDSDSD
)
)
See a php demo

I've this very pragmatic approach:
$spout_output = [
[
'id[0]' => 'BX-78',
'Name[0]' => 'XXX',
'Address[0]' => 'YUUSATD'
],
[
'id[1]' => 'BX-79',
'Name[1]' => 'YYY',
'Address[1]' => 'DHJSHDJGY'
],
[
'id[2]' => 'BX-80',
'Name[2]' => 'ZZZ',
'Address[2]' => 'DDSDSDA'
],
[
'id[3]' => 'BX-78',
'Name[3]' => 'AAA',
'Address[3]' => 'FSDSDS'
],
[
'id[4]' => 'BX-81',
'Name[4]' => 'XXX',
'Address[4]' => 'DSDSDSD'
]];
// store id to row, and name to row mappings.
// id and name will be keys, value will be an array of indexes of the array $spout_output
$id_to_rows = array();
$name_to_rows = array();
$duplicate_ids = array();
$duplicate_names = array();
foreach($spout_output as $row => $data)
{
$key_id = 'id['.$row.']';
$key_name = 'Name['.$row.']';
if(!isset($data[$key_id]))
continue;
$value_id = $data[$key_id];
$value_name = $data[$key_name];
if(!isset($id_to_rows[$value_id]))
{
$id_to_rows[$value_id] = array();
}
else
{
if(!isset($duplicate_ids[$value_id]))
{
$duplicate_ids[$value_id] = $id_to_rows[$value_id];
}
$duplicate_ids[$value_id][] = $row;
}
if(!isset($name_to_rows[$value_name]))
{
$name_to_rows[$value_name] = array();
}
else
{
if(!isset($duplicate_names[$value_name]))
{
$duplicate_names[$value_name] = $name_to_rows[$value_name];
}
$duplicate_names[$value_name][] = $row;
}
$id_to_rows[$value_id][] = $row;
$name_to_rows[$value_name][] = $row;
}
echo 'Duplicates:';
echo '<br>';
$shown_rows = array();
foreach($duplicate_ids as $id => $rows)
{
foreach($rows as $nr)
{
echo $id . '|' . $spout_output[$nr]['Name['.$nr.']'] . '|' . $spout_output[$nr]['Address['.$nr.']'];
echo '<br>';
$shown_rows[] = $nr;
}
}
foreach($duplicate_names as $name => $rows)
{
foreach($rows as $nr)
{
// if already shown above, skip this row
if(in_array($nr, $shown_rows))
continue;
echo $spout_output[$nr]['id['.$nr.']'] . '|' . $spout_output[$nr]['Name['.$nr.']'] . '|' . $spout_output[$nr]['Address['.$nr.']'];
echo '<br>';
$shown_rows[] = $nr;
}
}
Outputs:
Duplicates:
BX-78|XXX|YUUSATD
BX-78|AAA|FSDSDS
BX-81|XXX|DSDSDSD
I think your 'wanted output' contains an error in the address?
Anyway, with my code above I think you'll have enough mapped data to produce the output you want.

You could do something like this:
$dupes = [];
$current = [];
foreach ($array as $index => $entry) {
$idKey = "id[$index]";
$nameKey = "Name[$index]";
if (array_key_exists($entry[$idKey], $current)) {
$dupes[] = [$entry, $current[$entry[$idKey]]];
}
elseif (array_key_exists($entry[$nameKey], $current)) {
$dupes[] = [$entry, $current[$entry[$nameKey]]];
}
else {
$current[$entry[$idKey]] = $current[$entry[$nameKey]] = $entry;
}
}
print_r($dupes);
Which results in an array containing each set of duplicates (array of arrays):
Array
(
[0] => Array
(
[0] => Array
(
[id[3]] => BX-78
[Name[3]] => AAA
[Address[3]] => FSDSDS
)
[1] => Array
(
[id[0]] => BX-78
[Name[0]] => XXX
[Address[0]] => YUUSATD
)
)
[1] => Array
(
[0] => Array
(
[id[4]] => BX-81
[Name[4]] => XXX
[Address[4]] => DSDSDSD
)
[1] => Array
(
[id[0]] => BX-78
[Name[0]] => XXX
[Address[0]] => YUUSATD
)
)
)
Demo here: https://3v4l.org/JAtNU

In case someone of you are searching unique values by key.
function unique_multidim_array($array, $key) {
$temp_array = array();
$i = 0;
$key_array = array();
foreach($array as $val) {
if (!in_array($val[$key], $key_array)) {
$key_array[$i] = $val[$key];
$temp_array[$i] = $val;
}
$i++;
}
return $temp_array;
}
This function just takes multidimensional array and key value of field you need.
Then takes value of given array one by one (smaller arrays).
Then traverses given array and looking if taken key-value pair matches with given key.
After that if taken key-value pair matches with given key function just inserts smaller array in temporary array (array with unique values).
Don't forget to increment indexes of arrays ($i).
Then return array you got (with unique values) after function ends work.

Related

how to get the key and value in the object in list of array

How to get the distinct keys ($key) and multiple different values ($myObjectValues) in list of objects?
My expected outcome is distinct keys displays as column in table and its different values display as multiple rows. The column ($key) should not be hardcore and I plan to display in blade view.
Ideal:
Current Code:
foreach($x as $key => $item) {
print_r($key); //this is list number
foreach($item as $key => $myObjectValues){
print_r($key); //this is my object key
print_r($myObjectValues); //this is my object values
}
}
This is the json array object ($x).
Array(
[0] => stdClass Object
(
[milk_temperature] => 10
[coffeebean_level] => 124.022
)
[1] => stdClass Object
(
[milk_temperature] => 1099
[soya_temperature] => 10
[coffeebean_level] => 99.022
)
[2] => stdClass Object
(
[milk_temperature] => 1099
[coffeebean_level] => 99.022
)
)
You can do it like this, it's not the best approach in the world but it works and you can use it as an example. First you create a list with the table header titles and then start by printing the header and then the values.
<?php
$x = [
(object) [
'milk_temperature' => 10,
'coffeebean_level' => 124.022
],
(object) [
'milk_temperature' => 1099,
'soya_temperature' => 10,
'coffeebean_level' => 99.022
],
(object) [
'milk_temperature' => 1099,
'coffeebean_level' => 99.022
]
];
// list all the keys
$keys = [];
foreach($x as $key => $item) {
$keys = array_merge($keys, array_keys((array) $item));
}
$keys = array_unique($keys);
// echo the header
foreach ($keys as $key) {
echo $key . ' ';
}
echo "\n";
// echo the values
foreach($x as $item) {
foreach ($keys as $key) {
echo $item->$key ?? '-'; // PHP 7+ solution
// echo isset($item->$key) ? $item->$key : '-'; // PHP 5.6+
echo ' ';
}
echo "\n";
}
You can first get the keys of the array with array_keys() and array_collapse():
$columns = array_keys(array_collapse($records));
Then you look through the $records using the same loop you already have. Let's demo it with this example:
$columns = array_keys(array_collapse($records));
foreach($records as $key => $item) {
//these are each record
foreach ($columns as $column) {
//each column where you build the header
// converts $item to an array
$item = (array)$item;
if (! array_key_exists($column, (array)$item)) {
// show '---'
echo '---';
continue;
}
//show $item[$item]
echo $item[$column];
}
}
The great advantage of doing so i.e getting the columns first (apart from converting the stdClass to an array) is that the columns array can be used any way you deem fit.
It would be more beneficial if you can have your data all as array then you can easily use the array functions available on it.

How to create hierarchy in array?

I know I'm not describing well my question, but I want to create "nested array" as you can see:
folder/ -> folder/file.txt, folder/folder2/ -> folder/folder2/file.txt, folder/folder2/folder3/ -> etc
but instead, I get:
E:\wamp\www\index.php:31:
array (size=3)
'folder/' =>
array (size=1)
0 => string 'folder/file.txt' (length=15)
'folder/folder2/' =>
array (size=1)
0 => string 'folder/folder2/file.txt' (length=23)
'folder/folder2/folder3/' =>
array (size=1)
0 => string 'folder/folder2/folder3/file.txt' (length=31)
My code is:
$array = [
'folder/',
'folder/folder2/folder3/',
'folder/folder2/',
'folder/folder2/folder3/file.txt',
'folder/folder2/file.txt',
'folder/file.txt'
];
sort($array);
$array = array_flip($array);
function recursive_dir_nested($a) {
foreach ($a as $k => $v) {
if (preg_match("/\/$/", $k)) {
$a[$k] = [];
}
if (preg_match("/\/[^\/]+$/", $k)) {
$nk = preg_replace("/\/[^\/]+$/", "/", $k);
if (array_key_exists($nk, $a)) {
$a[$nk][] = $k;
unset($a[$k]);
} else {
recursive_dir_nested($a);
}
}
}
return $a;
}
I know I do something wrong, I'm not sure why... How can I solve this?
Not sure if using regex's is the best way to go. This builds on another answer - PHP - Make multi-dimensional associative array from a delimited string, but adds in the idea of using an array of entries. The one thing to note is that when adding new entries, if the element isn't currently an array, it turns it into an array so it can contain multiple entries ( the if ( !is_array($current) ) { part).
It uses each string and builds the folder hierarchy from that, saving the last part as the file name to be added specifically to the folder element...
$array = [
'folder/',
'folder/folder2/folder3/',
'folder/folder2/',
'folder/folder2/folder3/file.txt',
'folder/folder2/file.txt',
'folder/file.txt'
];
sort($array);
$output = [];
foreach ( $array as $entry ) {
$split = explode("/", $entry);
$current = &$output;
$file = array_pop($split);
foreach ( $split as $level ) {
if ( !isset($current[$level]) ){
if ( !is_array($current) ) {
$current = [ $current ];
}
$current[$level] = [];
}
$current = &$current[$level];
}
if ( !empty($file) ) {
$current = $file;
}
}
print_r($output);
This gives you...
Array
(
[folder] => Array
(
[0] => file.txt
[folder2] => Array
(
[0] => file.txt
[folder3] => file.txt
)
)
)
You can nest arrays in PHP. You might also want to use keys for the names of the directories:
$array = [
'folder' => [
'folder2' => [
'folder3' => [
'file.txt'
],
'file.txt'
],
'file.txt'
]
];
You could check each item with is_array() to see if it itself is array, then treat it as a string if it isn't.
See here for more info: php.net/manual/en/language.types.array.php

Compare multidimensional array with simple array

I have two arrays I want to compare these two arrays and find the match. If 807 and 200 appears in same keys like 131 then create third array
array(131=>(807,200));
array1:-
Array([0] => 807,[1] => 200)
array2 :-
$mat= Array([0] => Array([131] => 807),[1] => Array([132] => 807),[2] => Array([125] => 200),[3] => Array([131] => 200))
My code:
<?php
$filtered = array();
array_walk_recursive($matchingskusarr, function($val, $key) use(&$filtered) {
if ($key === 131) {
echo "The key $key has the value $val<br>";
$filtered[$val] = $key;
}
});
echo "<pre>";
print_r($filtered);
echo "</pre>";
?>
You can use array_column like this:
$filtered = array_column($mat, 131);
//Output: Array ( [0] => 807 [1] => 200 )
Update 1:
$matchingskusarr = Array( 0 => Array(131 => 807), 1 => Array(132 => 807),2 => Array(125 => 200),3 => Array(131 => 200)) ;
$skus = array(0=>807, 1=>200);
function yourMatch($skus, $matchingskusarr) {
$continue = [];
foreach ($matchingskusarr as $array) {
//Get the first key !!!
foreach ($array as $key => $value);
//Column already tested just continue
if(isset($continue[$key])) {
continue;
}
//New column we need to check if it matches our $skus
$column = array_column($matchingskusarr, $key);
if($column == $skus) {
return [$key => $column ];
}
$continue[$key] = true;
}
return null;
}
print_r(yourMatch($skus, $matchingskusarr));
Demo: https://3v4l.org/Cr2L4

Search in a multidimensional assoc array

I have an array, looking like this:
[lund] => Array
(
[69] => foo
)
[berlin] => Array
(
[138] => foox2
)
[tokyo] => Array
(
[180] => foox2
[109] => Big entrance
[73] => foo
)
The thing is that there were duplicate keys, so I re-arranged them so I can search more specifically, I thought.
Previously I could just
$key = array_search('foo', $array);
to get the key but now I don't know how.
Question: I need key for value foo, from tokyo. How do I do that?
You can get all keys and value of foo by using this:
foreach ($array as $key => $value) {
$newArr[$key] = array_search('foo', $value);
}
print_r(array_filter($newArr));
Result is:
Array
(
[lund] => 69
[tokyo] => 109
)
If you don't mind about the hard code than you can use this:
array_search('foo', $array['tokyo']);
It just a simple example, you can modify it as per your requirement.
Try this
$a = array(
"land"=> array("69"=>"foo"),
"land1"=> array("138"=>"foo1"),
"land2"=> array('180' => 'foox2',
'109' => 'Big entrance',
'73' => 'foo'),
);
//print_r($a);
$reply = search_in_array($a, "foo");
print_r($reply);
function search_in_array($a, $search)
{
$result = array();
foreach($a as $key1 => $array ) {
foreach($array as $k => $value) {
if($value == "$search") {
array_push($result,"{$key1}=>{$k}");
breck;
}
}
}
return $result;
}
This function will return the key or null if the search value is not found.
function search($searchKey, $searchValue, $searchArr)
{
foreach ($searchArr as $key => $value) {
if ($key == $searchKey && in_array($searchValue, $value)) {
$results = array_search($searchValue, $value);
}
}
return isset($results) ? $results : null;
}
// var_dump(search('tokyo', 'foo', $array));
Since Question: I need key for value foo, from tokyo. How do i do that?
$key = array_search('foo', $array['tokyo']);
As a function:
function getKey($keyword, $city, $array) {
return array_search($keyword, $array[$city]);
}
// PS. Might be a good idea to wrap this array in an object and make getKey an object method.
If you want to get all cities (for example to loop through them):
$cities = array_keys($array);
I created solution using array iterator. Have a look on below solution:
$array = array(
'lund' => array
(
'69' => 'foo'
),
'berlin' => array
(
'138' => 'foox2'
),
'tokyo' => array
(
'180' => 'foox2',
'109' => 'Big entrance',
'73' => 'foo'
)
);
$main_key = 'tokyo'; //key of array
$search_value = 'foo'; //value which need to be search
$iterator = new RecursiveIteratorIterator(new RecursiveArrayIterator($array));
foreach ($iterator as $key => $value) {
$keys = array();
if ($value == $search_value) {
$keys[] = $key;
for ($i = $iterator->getDepth() - 1; $i >= 0; $i--) {
$keys[] = $iterator->getSubIterator($i)->key();
}
$key_paths = array_reverse($keys);
if(in_array($main_key, $key_paths) !== false) {
echo "'{$key}' have '{$value}' value which traverse path is: " . implode(' -> ', $key_paths) . '<br>';
}
}
}
you can change value of $main_key and $serch_value according to your parameter. hope this will help you.
<?php
$lund = [
'69' => 'foo'
];
$berlin = [
'138' => 'foox2'
];
$tokyo = [
'180' => 'foox2',
'109' => 'Big entrance',
'73' => 'foo'
];
$array = [
$lund,
$berlin,
$tokyo
];
echo $array[2]['180']; // outputs 'foox2' from $tokyo array
?>
If you want to get key by specific key and value then your code should be:
function search_array($array, $key, $value)
{
if(is_array($array[$key])) {
return array_search($value, $array[$key]);
}
}
echo search_array($arr, 'tokyo', 'foo');
try this:
<?php
error_reporting(E_ALL | E_STRICT);
ini_set('display_errors', 'On');
$array=array("lund" => array
(
69 => "foo"
),
"berlin" => array
(
138 => "foox2"
),
"tokyo" => array
(
180 => "foox2",
109 => "Big entrance",
73 => "foo"
));
function search($array, $arrkey1, $arrvalue2){
foreach($array as $arrkey=>$arrvalue){
if($arrkey == $arrkey1){
foreach($arrvalue as $arrkey=>$arrvalue){
if(preg_match("/$arrvalue/i",$arrvalue2))
return $arrkey;
}
}
}
}
$result=search($array, "tokyo", "foo"); //$array=array; tokyo="inside array to check"; foo="value" to check
echo $result;
You need to loop through array, since its 2 dimensional in this case. And then find corresponding value.
foreach($arr as $key1 => $key2 ) {
foreach($key2 as $k => $value) {
if($value == "foo") {
echo "{$k} => {$value}";
}
}
}
This example match key with $value, but you can do match with $k also, which in this case is $key2.

How to group by and sum a multi-dimensional array?

I have already seen this
stackoverflow page but it is not helping me.
I want to group by two columns and sum the values of a third column.
If the discount_id and dis_percent are the same then add the discount_value.
Here is my array:
$dis = [
[['Dis_id' => 'Dl-Dis1'], ['Dis_per' => '7.500'], ['Dis_val' => '192.75']],
[['Dis_id' => 'Dl-Dis2'], ['Dis_per' => '2.500'], ['Dis_val' => '97.88']],
[['Dis_id' => 'Dl-Dis1'], ['Dis_per' => '5.000'], ['Dis_val' => '39.90']],
[['Dis_id' => 'Dl-Dis2'], ['Dis_per' => '2.500'], ['Dis_val' => '99.90']]
];
The output that I need is:
D1-Dis1->7.5->192.75
D1-Dis1->5.0->39.9
D1-Dis2->2.5->197.78
My code looks like this:
$newarr = array();
$reverse_map = array();
foreach($dis as $idx => $entry) {
if (isset($reverse_map[$entry['Dis_id']])) {
// have we seen this name before? retrieve its original index value
$idx = $reverse_map[$entry['Dis_id']];
} else {
// nope, new name, so store its index value
$reverse_map[$entry['Dis_id']] = $idx;
}
// copy the 'constant' values
$newarr[$idx]['Dis_id'] = $entry['Dis_id'];
$newarr[$idx]['Dis_per'] = $entry['Dis_per'];
// sum the qtd_post values to whatever we previously stored.
foreach($entry['Dis_val'] as $x => $y) {
$newarr[$idx]['Dis_val'][$x] += $y;
}
}
This is the solution I've come up with based off of the understanding that your intended array structure was as so;
$dis = array(
array(
'Dis_id' => 'Dl-Dis1',
'Dis_per' => 7.500,
'Dis_val' => 192.75
),
...
);
It determines the solution by creating a multidimensional array where the first dimension is the Dis_id, and the second dimension is the Dis_per, and the value becomes the sum of the Dis_val;
$sums = array();
foreach ($dis as $entry) {
if (!isset($sums[$entry['Dis_id']])) {
$sums[$entry['Dis_id']] = array();
}
if (!isset($sums[$entry['Dis_id']]["{$entry['Dis_per']}"])) {
$sums[$entry['Dis_id']]["{$entry['Dis_per']}"] = 0;
}
$sums[$entry['Dis_id']]["{$entry['Dis_per']}"] += $entry['Dis_val'];
}
See this working example; https://eval.in/158661
As you iterate your input array, you will need to isolate the Dis_id and Dis_per values and use them as keys when storing your Dis_val.
If there no value present for a [Dis_id][Dis_per] element, then you can simply store the value. If there is a pre-existing value, you need to add the new value to the stored value. isset() is most efficient function to aid in the identification of new/existing elements in the result array.
Code: (Demo)
$dis = [
[['Dis_id' => 'Dl-Dis1'], ['Dis_per' => '7.500'], ['Dis_val' => '192.75']],
[['Dis_id' => 'Dl-Dis2'], ['Dis_per' => '2.500'], ['Dis_val' => '97.88']],
[['Dis_id' => 'Dl-Dis1'], ['Dis_per' => '5.000'], ['Dis_val' => '39.90']],
[['Dis_id' => 'Dl-Dis2'], ['Dis_per' => '2.500'], ['Dis_val' => '99.90']]
];
foreach ($dis as $group) {
$id = $group[0]['Dis_id'];
$per = $group[1]['Dis_per'];
if (!isset($result[$id][$per])) {
$result[$id][$per] = $group[2]['Dis_val'];
} else {
$result[$id][$per] += $group[2]['Dis_val'];
}
}
var_export($result);
Output:
array (
'Dl-Dis1' =>
array (
'7.500' => '192.75',
'5.000' => '39.90',
),
'Dl-Dis2' =>
array (
'2.500' => 197.78,
),
)

Categories