Concat values of array with same key - php

I want to concat values of array with same key
Example:
[0] => Array
(
[0] => A
[1] => XYZ
)
[1] => Array
(
[0] => B
[1] => ABC
)
[2] => Array
(
[0] => A
[1] => LMN
)
[3] => Array
(
[0] => B
[1] => PQR
)
)
Expected output:
[0] => Array
(
[0] => A
[1] => XYZ,LMN
)
[1] => Array
(
[0] => B
[1] => ABC,PQR
)
)

A simple solution uses the PHP function array_reduce():
// The input array you posted in the question
$input = array(
array('A', 'XYZ'),
array('B', 'ABC'),
array('A', 'LMN'),
array('B', 'PQR'),
);
// Reduce the array to a new array that contains the data aggregated as you need
$output = array_reduce(
// Process each $item from $input using a callback function
$input,
// The callback function processes $item; the partial result is $carry
function (array $carry, array $item) {
// Extract the key into a variable
$key = $item[0];
// If the key was encountered before
// then a partial entry already exists in $carry
if (isset($carry[$key])) {
// Append the desired value to the existing entry
$carry[$key][1] .= ','.$item[1];
} else {
// Create a new entry in $carry (copy $item to key $key for quick retrieval)
$carry[$key] = $item;
}
// Return the updated $carry
return $carry;
},
// Start with an empty array (it is known as $carry in the callback function)
array()
);
// $output contains the array you need

Try this:
$final = array();
foreach ($array_items as $item)
{
$key = $item[0];
$found_index = -1;
for ($i=0; $i<count($final); $i++)
{
if ($key == $final[$i][0])
{
$found_index = $i;
break;
}
}
if ($found_index == -1)
{
$final_item = array();
$final_item[0] = $key;
$final_item[1] = $item[1];
$final[] = $final_item;
}
else
{
$final[$found_index][1] .= ",".$item[1];
}
}
We create a new array $final, and loop through your old array $array_items. For each item, we see if there is already an item in $final that has the same [0] index. If it doesn't exist, we create it and add the initial string to the [1] index. If it does exist, we just have to add the string onto the end of the [1] index.
Try it, substituting $array_items for whatever your array is called, let me know if it works.

Check my solution. It should work fine. I hope it will help you much.
$result = $passed_keys = $extended_arr = [];
foreach ($arr as $k => $value) {
for($i = $k + 1; $i < count($arr); $i++){
if ( $value[0] == $arr[$i][0] ){ // compare each array with rest subsequent arrays
$key_name = $value[0];
if (!array_key_exists($key_name, $result)){
$result[$key_name] = $value[1] .",". $arr[$i][1];
} else {
if (!in_array($i, $passed_keys[$key_name])) {
$result[$key_name] .= ",". $arr[$i][1];
}
}
$passed_keys[$key_name][] = $i; // memorizing keys that were passed
}
}
}
array_walk($result, function($v, $k) use(&$extended_arr){
$extended_arr[] = [$k, $v];
});
The result is in $extended_arr

My solution, creates a custom key which makes identifying the letter much easier. This removes the need to continuously iterate through each array, which can become a major resources hog.
<?php
$inital_array = array(
array('A','XYZ'),
array('B','ABC'),
array('A','LMN'),
array('B','PQR')
);
$concat_array = array();
foreach($inital_array as $a){
$key = $a[0];
if( !isset($concat_array[$key]) ){
$concat_array[$key] = array($key,'');
}
$concat_array[$key][1] .= (empty($concat_array[$key][1]) ? '' : ',').$a[1];
}
$concat_array = array_values($concat_array);
echo '<pre>',print_r($concat_array),'</pre>';

Related

Split flat array into grouped subarrays containing values from consecutive key in the input array

I have an array from array_diff function and it looks like below:
Array
(
[0] => world
[1] => is
[2] => a
[3] => wonderfull
[5] => in
[6] => our
)
As you can see, we have a gap between the keys #3 and #5 (i.e. there is no key #4).
How can I split that array into 2 parts, or maybe more if there are more gaps?
The expected output would be:
Array
(
[0] => Array
(
[0] => world
[1] => is
[2] => a
[3] => wonderfull
)
[1] => Array
(
[0] => in
[1] => our
)
)
You can use old_key,new_key concept to check that there difference is 1 or not? if not then create new index inside you result array otherwise add the values on same index:-
<?php
$arr = Array(0 => 'world',1 => 'is',2 => 'a',3 => 'wonderfull',5 => 'in',6 => 'our');
$new_array = [];
$old_key = -1;
$i = 0;
foreach($arr as $key=>$val){
if(($key-$old_key) ==1){
$new_array[$i][] = $val;
$old_key = $key;
}
if(($key-$old_key) >1){
$i++;
$new_array[$i][] = $val;
$old_key = $key;
}
}
print_r($new_array);
https://3v4l.org/Yl9rp
You can make use of the array internal pointer to traverse the array.
<?php
$arr = Array(0=>"world",1=>"is",2=>"a",3=>"wonderfull",5=>"in",6=>"our");
print_r($arr);
$result = Array();
$lastkey;
while($word = current($arr))
{
$key = key($arr);
if(isset($lastkey) && $key == $lastkey + 1)
{
$result[count($result) - 1][] = $word;
}
else
{
$result[] = Array($word);
}
$lastkey = $key;
next($arr);
}
print_r($result);
?>
This task is a perfect candidate for a reference variable. You unconditionally push values into a designated "bucket" -- in this case a subarray. You only conditionally change where that bucket is in the output array.
There are two important checks to make when determining if a new incremented key should be generated:
if it is not the first iteration and
the current key minus (the previous key + 1) does not equal 0.
Code: (Demo)
$nextKey = null;
$result = [];
foreach ($array as $key => $val) {
if ($nextKey === null || $key !== $nextKey) {
unset($ref);
$result[] = &$ref;
}
$ref[] = $val;
$nextKey = $key + 1;
}
var_export($result);
This solution generates an indexed array starting from zero with my sample input and uses only one if block. In contrast, AliveToDie's solution generates a numerically keyed array starting from 1 and uses two condition blocks containing redundant lines of code.

PHP: How to iterate over array to retrieve incremental value (n, n+1, etc)

I have an array like this
$data = array(51729,49359,47548,8242,8124,8716,19610,18030,15698);
And a index number
$index = 3;
Is there a simple way to iterate over $data to get
$first = array(51729,8242,19610)
$second = array(49359,8124,18030)
$third = array(47548,8716,15698)
Is array_chunk() and then foreach the chunks the way to go?
Edit: Here is how I made it with array_chunk()
$data = array(51729,49359,47548,8242,8124,8716,19610,18030,15698);
$index = 3;
$chunks = array_chunk($data, $index);
$first = array();
$second = array();
$third = array();
foreach($chunks as $out) {
$first[] = $out[0];
$second[] = $out[1];
$third[] = $out[2];
}
Edit 2: All of this is part of transforming a unidimensional array to multidimensional, naming will eventually be dynamic, based on values in array $labels. Index number is also going to change ($index is provided in original array).
To do it dynamically, you can create a multidimensional array.
// Your provided values
$data = array(51729,49359,47548,8242,8124,8716,19610,18030,15698);
$index = 3;
// Initialize your result array
$result = array();
// Loop through the array
foreach ($data as $key => $value) {
// Assign every nth to the appropriate sub-array
$result[$key % $index][] = $value;
}
// Print for validation
print_r($result);
Provides:
Array
(
[0] => Array
(
[0] => 51729
[1] => 8242
[2] => 19610
)
[1] => Array
(
[0] => 49359
[1] => 8124
[2] => 18030
)
[2] => Array
(
[0] => 47548
[1] => 8716
[2] => 15698
)
)
If you are looking to do this non-dynamically. i.e. it will always be named this way, you're looking to continuously iterate in this manner with little changing, then the following code will be sufficient.
$i = 1;
foreach($data as $arr){
switch($i){
case 1:
array_push($first, $arr);
break;
case 2:
array_push($second, $arr);
break;
case 3:
array_push($third, $arr);
break;
}
$i++;
if($i == 4){
$i = 1;
}
}

Get path from adjacency list data

I have an array (data from adjacency table) and it looks like:
Array
(
[0] => Array
(
[id] => 1
[name] => Anniversary
[parent] => 0
)
[1] => Array
(
[id] => 12
[name] => New arrives
[parent] => 1
)
[2] => Array
(
[id] => 13
[name] => Discount
[parent] => 12
)
[3] => Array
(
[id] => 6
[name] => Birthday
[parent] => 0
)
)
And I'm looking for the way to retrieve my path by ID;
For example: getPath(13): Anniversary->New arrives->Discount;
For example: getPath(12): Anniversary->New arrives;
For example: getPath(1): Anniversary;
For example: getPath(6): Birthday;
How can I do this?
Thanks!
function getpath($id, $arr, $level = 0) {
$result = array();
foreach($arr as $key => $value){
if($id == $value['id']){
$result[] = $value['name'];
$id = $value['parent'];
if($id != 0){
$result = array_merge($result, getpath($id, $arr, $level+1));
}else{
break;
}
}
}
return $level ? $result : implode('->',array_reverse($result));
}
echo getpath(13,$arr);
Consider this array,
$input = [
['id'=>1, 'name'=>'Anniversary', 'parent'=>0],
['id'=>12, 'name'=>'New arrives', 'parent'=>1],
['id'=>13, 'name'=>'Discount', 'parent'=>12],
['id'=>6, 'name'=>'Birthday', 'parent'=>0]
];
and this function,
function path($element_id, $input, $ids = [])
{
if(!$ids) // for performance, make this once and pass it around
{
$ids = array_column($input, 'id'); // array containing only 'id's of $input
}
$current_key = array_search($element_id, $ids); // search for $input variable's current key
unset($ids[$current_key]); // unsetting used keys to make above array search faster next time
$current_element = $input[$current_key]; // get current element as array from $input
$names[] = $current_element['name']; // create an array containing current element
if($current_element['parent'] != 0) // check if current element have parent
{
$names[] = path($current_element['parent'], $input, $ids); // call this function, let it return string, append it to $names
}
return implode(' ⟶ ', array_reverse($names)); // make final return, seprate by ⟶
}
Reading echo path(13, $input); will return
Anniversary ⟶ New arrives ⟶ Discount
Here is minified version of the same function
function path($a,$b,$c=[]){if(!$c){$c=array_column($b,'id');}$d=array_search($a,$c);unset($c[$d]);$e=$b[$d];$f[]=$e['name'];if($e['parent']!=0){$f[]=path($e['parent'],$b,$c);}return implode(' ⟶ ',array_reverse($f));}
Thanks to code reviewers Loufylouf and Quill
$find = 13;
$path = array();
function FindById ($arr, $find) {
$k = null;
foreach($arr as $key => $item)
if ($item['id'] == $find)
{ $k = $key; break; }
return $k;
}
if ( false === ($k = FindById($arr, $find))) die("not found");
while (true) {
array_unshift($path, $arr[$k]['name']);
if( ! $arr[$k]['parent']) break;
if(false === ($k = FindById($arr, $arr[$k]['parent']))) die("illegal structure");
}
echo implode('->', $path);

Group data by multiple fields dynamically in PHP

I want to group an associative array by fields. The array itself is originally from a mysql database query.
Below is an example of how I do it by hard coding it:
<?php
$fields = array("ID,subID");
$fieldCounts = count($fields);
$data = array(); //there is sql querieed data
$parsedData = array();
foreach ($data as $val)
{
if ($fieldCounts == 1)
{
$f0 = $fields[0];
$fv0 = $val[$f0];
$parsedData[$fv0][] = $val;
}
else if ($fieldCounts == 2)
{
$f0 = $fields[0];
$fv0 = $val[$f0];
$f1 = $fields[10];
$fv1 = $val[$f1];
$parsedData[$fv0][$f1][] = $val;
}
else
{
exit("Third field not implemented");
}
}
?>
But how can I do it dynamically with an arbitrary number of fields?
Am not sure how this code has worked for you but some things are that wrong and might not allow the code to function properly
Fields has only as one valued with ,
$fields = array("ID,subID");
^----------- between string
Instead of
$fields = array("ID","subID");
Notice: Undefined offset:
$f1 = $fields[10];
^----- your array is not up to 10
Since you did not put your generate data and desired output. I would assume your final output and generate some temporary data
$fields = array("ID","subID"); //You can Increase or decrease this Fields
$fieldCounts = count($fields);
$data = array(); // there is sql querieed data
for($i = 0; $i < 3; $i ++) {
$data[] = array("ID" => mt_rand(1, 1000),"subID" => "sub" . mt_rand(100, 900));
}
Ruining your code with the 2 corrections above
foreach ( $data as $val ) {
if ($fieldCounts == 1) {
$f0 = $fields[0];
$fv0 = $val[$f0];
$parsedData[$fv0][] = $val;
} else if ($fieldCounts == 2) {
$f0 = $fields[0];
$fv0 = $val[$f0];
$f1 = $fields[1];
$fv1 = $val[$f1];
$parsedData[$fv0][$f1][] = $val;
} else {
exit("Third field not implemented");
}
}
Output
Array
(
[159] => Array
(
[subID] => Array <----------- SubID is fixed in your can cause confict
(
[0] => Array
(
[ID] => 159
[subID] => sub589
)
)
)
[334] => Array
(
[subID] => Array
(
[0] => Array
(
[ID] => 334
[subID] => sub703
)
)
)
)
A better Alternative to yours
$parsedData = array();
foreach ( $data as $val ) {
$temp = &$parsedData;
foreach ( array_slice($val, 0, $fieldCounts) as $key ) {
$temp = &$temp[$key];
}
$temp[] = $val;
}
print_r($parsedData);
Output
Array
(
[159] => Array
(
[sub589] => Array <---------- Make Sub ID Dynamic
(
[0] => Array
(
[ID] => 159
[subID] => sub589
)
)
)
[334] => Array
(
[sub703] => Array
(
[0] => Array
(
[ID] => 334
[subID] => sub703
)
)
)
)
Recommended Version For easy array path
$parsedData = array();
foreach ( $data as $val ) {
$temp = &$parsedData;
foreach ( array_slice($val, 0, $fieldCounts) as $key ) {
$temp = &$temp[$key];
}
$temp = $val;
}
print_r($parsedData);
Output
Array
(
[159] => Array
(
[sub589] => Array <---- Easy to asses as $parsedData['159']['sub589']
(
[ID] => 159
[subID] => sub589
)
)
[334] => Array
(
[sub703] => Array
(
[ID] => 334
[subID] => sub703
)
)
)
Instead of doing if/elseif/else inside your $data foreach-loop (which is always limited to the number you "write" in there with that structure and a lot of code-duplicateion) you need to turn that if/elseif/else into a loop of it's own.
But first of all transform the existing code, I start in the first if body, it contains already all code necessary:
$f0 = $fields[0];
$fv0 = $val[$f0];
$parsedData[$fv0][] = $val;
The $val should be assigned to the array $parsedData which is keyed by $fields name $value. Let's compress this here, the number 0 in names is superfluous as we don't want it any longer (but maybe the first):
$field = $fields[0];
$value = $values[$field];
$parsedData[$value][] = $values;
(I changed $val into $values to improve naming). This is now more easy to read and understand. Also we spot the magic number 0 here more easily.
Now to the magic. We want to add to an array here (push):
$parsedData[$value][] = $values;
To make this more easy, let's turn it this way:
$array = &$parsedData[$value];
$array[] = $values;
This right now seems superfluous, but when this turns into a loop, it will become more clear:
$array = &$parsedData;
...
$array = &array[$value];
...
$array[] = $values;
Let's review the code in with the outer loop at this moment:
foreach ($data as $values)
{
$array = &$parsedData;
$field = $fields[0];
$value = $values[$field];
$array = &$array[$value];
$array[] = $values;
}
Obviously this code is yet not complete. The inner-loop is missing but it starts to get some kind of body. And actually the inner loop is pretty simple to achieve:
$array = &$parsedData;
foreach ($fields as $field)
{
$value = $values[$field];
$array = &$array[$value];
}
$array[] = $values;
And that's already it. The single field has been turned into an iteration over all fields. The aliasing/referencing of the sub-array per each step in the iteration allows to push the value to the appropriate array entry after the inner loop has finished.
The whole outer and inner loop:
foreach ($data as $values)
{
$array = &$parsedData; # set reference
foreach ($fields as $field)
{
$value = $values[$field];
$array = &$array[$value];
}
$array[] = $values;
unset($array); # remove reference
}

php multidimensional array from known key values

I have a collection of keys in this massive flat single array I would like to basically expand that array into a multidimensional one organized by keys - here is an example:
'invoice/products/data/item1'
'invoice/products/data/item2'
'invoice/products/data/item2'
=>
'invoice'=>'products'=>array('item1','item2','item3')
how can I do this - the length of the above strings are variable...
Thanks!
$src = array(
'invoice/products/data/item1',
'invoice/products/data/item2',
'invoice/products/data/item2',
'foo/bar/baz',
'aaa/bbb'
);
function rsplit(&$v, $w)
{
list($first, $tail) = explode('/', $w, 2);
if(empty($tail))
{
$v[] = $first;
return $v;
}
$v[$first] = rsplit($v[$first], $tail);
return $v;
}
$result = array_reduce($src, "rsplit");
print_r($result);
Output is:
Array (
[invoice] => Array
(
[products] => Array
(
[data] => Array
(
[0] => item1
[1] => item2
[2] => item2
)
)
)
[foo] => Array
(
[bar] => Array
(
[0] => baz
)
)
[aaa] => Array
(
[0] => bbb
)
)
Something along these lines: (Didn't test it though!) Works now ;)
$data = array();
$current = &$data;
foreach($keys as $value) {
$parts = explode("/", $value);
$parts_count = count($parts);
foreach($parts as $i => $part) {
if(!array_key_exists($part, $current)) {
if($i == $parts_count - 1) {
$current[] = $part;
}
else {
$current[$part] = array();
$current = &$current[$part];
}
}
else {
$current = &$current[$part];
}
}
$current = &$data;
}
$keys beeing the flat array.
Although it's not clear from your question how the "/" separated strings will map to an array, the basic approach will probably be something like this:
$result = array();
$k1 = $k2 = '';
ksort($yourData); // This is the key (!)
foreach ($yourData as $k => $v) {
// Use if / else if / else if to watch for new sub arrays and change
// $k1, $k2 accordingly
$result[$k1][$k2] = $v;
}
This approach uses the ksort to ensure that keys at the same "level" appear together, like this:
'invoice/products/data1/item1'
'invoice/products/data1/item2'
'invoice/products/data2/item3'
'invoice/products2/data3/item4'
'invoice/products2/data3/item5'
Notice how the ksort corresponds to the key grouping you're aiming for.

Categories