I have a a number of values/IDs that need to be translated to a single ID, what is the recommended method using PHP?
For example, I want IDs 38332, 84371, 37939, 1275 to all translate to ID 1234 and IDs222, 47391, 798 to all translate to ID 1235, etc. .
I'm thinking PHP has something built-in to handle this efficiently?
I'm thinking PHP has something built-in to handle this efficiently?
You can use the standard array as a map, quickly translating one ID to another:
$table[38332]; # int(1234)
depending on how you store your overall translation table, you can create a function that returns the translation from its input:
$table = $translation('I want IDs 38332, 84371, 37939, 1275 to all translate to ID 1234');
$result = $table[1275] ?? null; # int(1234)
Example:
$parseId = static fn(string $i) => (int)trim($i);
$translation = static fn(string $buffer): array
=> preg_match_all('~((?:\d+,\s*)+\d+)\s+to all translate to ID\s*(\d+)~', $buffer, $_, PREG_SET_ORDER)
? array_reduce($_, static fn (array $carry, array $item): array => [
$ids = array_map($parseId, explode(',', $item[1])),
$carry += array_fill_keys($ids, $parseId($item[2])),
$carry,][2], []) : [];
This is pretty easy to accomplish with PHP, here's one way you could do it:
Using this method, you populate the $map array, using the id you want to replace with as the key, and the value being an array of the keys you want to be replaced. It then calculates a simple key => value array based on this to make comparison a lot quicker.
Instead of creating a copy of the data, you could use foreach ($data as &$record)
$data = [
[
'id' => 1,
'foreign_id' => 38332,
'text' => 'a'
],
[
'id' => 2,
'foreign_id' => 84371,
'text' => 'b'
],
[
'id' => 3,
'foreign_id' => 37939,
'text' => 'c'
],
[
'id' => 4,
'foreign_id' => 1275,
'text' => 'd'
],
[
'id' => 5,
'foreign_id' => 222,
'text' => 'e'
],
[
'id' => 5,
'foreign_id' => 47391,
'text' => 'f'
],
[
'id' => 5,
'foreign_id' => 798,
'text' => 'g'
]
];
$map = [
123 => [
38332,
84371,
37939,
1275
],
1235 => [
222,
47391,
798
]
];
// Calculate a map to speed things up later
$map_calc = [];
foreach ($map as $destination_id => $ids) {
foreach ($ids as $id) {
$map_calc[$id] = $destination_id;
}
}
$new_data = [];
foreach ($data as $record) {
if (isset($map_calc[$record['foreign_id']]))
$record['foreign_id'] = $map_calc[$record['foreign_id']];
$new_data[] = $record;
}
var_dump($new_data);
Related
I am trying to extract a value from an array by searching by another value.
I have the uri value and I require the playcount value that corresponds with the uri.
What is the best approach to traverse this multi-level array and return the desired data?
My current code:
$decoded = json_decode($response, true);
$trackids = 'spotify:track:'. $trackid .'';
$key = array_search($trackids, array_column($decoded, 'playcount'));
$result = $decoded[$key]['playcount'];
echo "Result: ";
echo $result;
I think it is incomplete and not sure how to proceed from there as it doesn't work.
The $decoded array:
$decoded = [
'success' => 1,
'data' => [
'uri' => 'spotify:album:3T4tUhGYeRNVUGevb0wThu',
'name' => 'รท (Deluxe)',
'cover' => [
'uri' => 'https://i.scdn.co/image/ab67616d00001e02ba5db46f4b838ef6027e6f96'
],
'year' => 2017,
'track_count' => 16,
'discs' => [
[
'number' => 1,
'name' => null,
'tracks' => [
[
'uri' => 'spotify:track:7oolFzHipTMg2nL7shhdz2',
'playcount' => 181540969,
'name' => 'Eraser',
'popularity' => 63,
'number' => 1,
'duration' => 227426,
'explicit' => null,
'playable' => 1,
'artists' => [
[
'name' => 'Ed Sheeran',
'uri' => 'spotify:artist:6eUKZXaKkcviH0Ku9w2n3V',
'image' => [
'uri' => 'https://i.scdn.co/image/ab6761610000517412a2ef08d00dd7451a6dbed6'
]
]
]
],
[
'uri' => 'spotify:track:6PCUP3dWmTjcTtXY02oFdT',
'playcount' => 966197832,
'name' => 'Castle on the Hill',
'popularity' => 79,
'number' => 2,
'duration' => 261153,
'explicit' => null,
'playable' => 1,
'artists' => [
[
'name' => 'Ed Sheeran',
'uri' => 'spotify:artist:6eUKZXaKkcviH0Ku9w2n3V',
'image' => [
'uri' => 'https://i.scdn.co/image/ab6761610000517412a2ef08d00dd7451a6dbed6'
]
]
]
]
]
]
]
]
];
$key = array_search($trackids, array_column($decoded, 'playcount')); in this line, you make two mistake. First, there is no column like playcount in $decode array. Second, you are searching with uri key, not playcount key. There is one more thing, discs key and track inside discs key, both are multidimensional array. So if you want to fetch the exact value, This query will be,
$decoded = array_map(function($x) {
return array_column($x, 'url');
}, array_column($decoded['data']['discs'], 'tracks'));
$decoded = call_user_func_array('array_merge', $decoded);
$key = array_search($trackids, $decoded);
You're on the right track, but you need to dig down deeper into the array structure to search for the tracks within each disc.
$decoded = json_decode($response, true);
$trackid = '7oolFzHipTMg2nL7shhdz2';
$trackidString = 'spotify:track:' . $trackid;
$playcount = null;
// Loop through the discs looking for the track
foreach ($decoded['data']['discs'] as $currDisc)
{
// If we find the track, get the playcount and break out of the loop
$key = array_search($trackidString, array_column($currDisc['tracks'], 'uri'));
if($key !== false)
{
$playcount = $currDisc['tracks'][$key]['playcount'];
break;
}
}
assert($playcount == 181540969, 'We should find the expected playcount value');
echo 'Result: ' . $playcount . PHP_EOL;
I see some inefficient usages of array functions for a task that is cleanest and most performantly performed with classic nested loops. When searching traversable structures for a solitary qualifying value, it is not best practice to use array functions because they do not permit "short-circuiting" or a break/return. In other words, array functions are a poor choice when you do not need to iterate the entire structure.
While traversing your array structure, whenever you encounter indexed keys on a given label, you should implement another foreach(). There are only 2 levels with indexes on your way to the targeted uri and playcount values, so only 2 foreach() loops are needed. When you find your targeted uri, extract the value then break your loop.
Code: (Demo)
$needle = 'spotify:track:7oolFzHipTMg2nL7shhdz2';
foreach ($decoded['data']['discs'] as $disc) {
foreach ($disc['tracks'] as $track) {
if ($track['uri'] === $needle) {
echo $track['playcount'];
break;
}
}
}
I am trying to add a new value to the array (I know it is possible with array_map() but I would like to test it with the array_walk()).
This is the code:
$array = [
[
'id' => 1,
'name' => 'Jesus',
],
[
'id' => 2,
'name' => 'David',
],
];
And I want this output:
$array = [
[
'id' => 1,
'name' => 'Jesus',
'locked' => 0,
],
[
'id' => 2,
'name' => 'David',
'locked' => 0,
],
];
I tried with the following code:
array_walk($array, static function(array $item): array {
$item += ['locked' => 0];
//var_dump($item); // Here the array has the three values.
return $item;
});
// Also I tried the same code but not returning the array, I mean:
array_walk($array, static function(array $item): void {
$item += ['locked' => 0];
//var_dump($item); // Here the array has the three values.
});
Is it possible what I want with an array_walk()?
That would be the solution with an array_map().
$arrayMapped = array_map(static function(array $item): array {
return $item += ['locked' => 0];
}, $array);
var_dump($arrayMapped);
Cheers!
Arrays are passed by value. You need to define the argument by reference using &
array_walk($array, function(array &$item): void {
$item['locked'] = 0;
});
I have an array in which i want to combine the common fields together.
So that the common names get grouped together and they contain the common values of that particular name.
Below is my array
[
[
count_employee: 2,
name: "Harry"
],
[
count_employee: 61,
name: "Jack"
],
[
count_employee: 11,
name: "John"
],
[
count_warehouse: 1,
name: "Harry"
],
[
count_warehouse: 77,
name: "John"
],
[
count_warehouse: 45,
name: "Jack"
]
]
I want the output to be
[
[
name: "Harry",
count_employee:2
count_warehouse:1
],
[
name: "Jack",
count_employee: 61
count_warehouse: 45
],
[
name: "John",
count_employee:11
count_warehouse:77
],
]
So far i have tried this out
foreach ($data as $key => $value) {
$group[$value['name']]['name'] = $value['name'];
$group[$value['name']][$key]['count_employee'] = $value['count_employee'];
$group[$value['name']][$key]['count_warehouse'] = $value['count_warehouse'];
}
Loop over the array, and use the name as the index of each element. If no element by that index does not exist, define it as an empty array.
Then all you need to do is add the fields (if they exist) to the proper column.
Your issue was that you were creating an array with one too many dimensions, instead of adding each value to the current count - in addition to the fact that you will not always have both the warehouse and employee count defined for each iteration.
$result = [];
foreach ($arr as $v) {
// Initialize the person
if (!isset($result[$v['name']])) {
$result[$v['name']] = ["name" => $v['name'], "count_employee" => 0, "count_warehouse" => 0];
}
// Add values
if (isset($v['count_employee']))
$result[$v['name']]['count_employee'] += $v['count_employee'];
if (isset($v['count_warehouse']))
$result[$v['name']]['count_warehouse'] += $v['count_warehouse'];
}
// print_r($result);
Live demo at https://3v4l.org/TeNKF
You can use array_walk with array_key_exists
$res=[];
array_walk($arr, function($v,$k) use (&$res){
if(array_key_exists($v['name'], $res))
$res[$v['name']]['count_warehouse'] = $v['count_warehouse'];
else
$res[$v['name']] = $v;
});
print_r(array_values($res));
Live Demo
Assuming all the array has a name key for each sub array contained within, we can use the name as a key for an array and merge arrays with corresponding name values.
<?php
$data=
[
[
'count_employee'=> 2,
'name'=> 'Harry'
],
[
'count_employee'=> 61,
'name'=> 'Jack'
],
[
'count_employee'=> 11,
'name'=> 'John'
],
[
'count_warehouse'=> 1,
'name'=> 'Harry'
],
[
'count_warehouse'=> 77,
'name'=> 'John'
],
[
'count_warehouse'=> 45,
'name'=> 'Jack'
]
];
$output = [];
foreach($data as $item) {
$output[$item['name']] = array_merge($item, $output[$item['name']] ?? []);
}
var_export($output);
Output:
array (
'Harry' =>
array (
'count_warehouse' => 1,
'name' => 'Harry',
'count_employee' => 2,
),
'Jack' =>
array (
'count_warehouse' => 45,
'name' => 'Jack',
'count_employee' => 61,
),
'John' =>
array (
'count_warehouse' => 77,
'name' => 'John',
'count_employee' => 11,
),
)
I need to add new elemets to my array when a new category value is encountered. When a category value is encountered after the first time, its value1 and value2 values should be added to the first encounter's respective values.
Also, in the result array, I no longer wish to keep the category column. The category-grouping rows should use the category value as its name value.
Sample input:
$datas = [
[
'category' => 'Solution',
'name' => 'Name1',
'value1' => 20,
'value2' => 21
],
[
'category' => 'Solution',
'name' => 'Name2',
'value1' => 30,
'value2' => 31
],
[
'category' => 'Solution1',
'name' => 'Name3',
'value1' => 40,
'value2' => 41
]
];
Desired result:
[
['name' => 'Solution', 'value1' => 50, 'value2' => 52],
['name' => 'Name1', 'value1' => 20, 'value2' => 21],
['name' => 'Name2', 'value1' => 30, 'value2' => 31],
['name' => 'Solution1', 'value1' => 40, 'value2' => 41],
['name' => 'Name3', 'value1' => 40, 'value2' => 41]
]
I tried like this:
private function groupByProductSuperCategory($datas)
{
$return = [];
foreach ($datas as $data) {
$return[$data['category']][$data['name']] = array_sum(array_column('category', $data);
}
return $return;
}
The idea is to calculate first all sum values for by category, and after that just put values from name like another array. Have you an idea of how to do that?
From the posted array... To end in the desired array, there is some tiny fixes to do first. But I assumed it was due to typos while copying here...
So here is the array I started with:
$result = [
0 => [
"category" => 'Solution',
"name" => 'Name1',
"value1" => 20,
"value2" => 21
],
1 => [
"category" => 'Solution',
"name" => 'Name2',
"value1" => 30,
"value2" => 31
],
2 => [
"category" => 'Solution1',
"name" => 'Name3',
"value1" => 40,
"value2" => 41
]
];
Now, that re-organization of the data is a bit more complex than it looks... You need to perform several loops to:
Find distinct "category" names
Perform the summations for each
Add the sum item and the single items
So here is the code I ended with:
function groupByProductSuperCategory($datas){
$category = [];
$return = [];
// Find distinct categories
foreach ($datas as $data) {
if(!in_array($data["category"],$category)){
array_push($category,$data["category"]);
}
}
// For each distinct category, add the sum item and the single items
foreach ($category as $cat) {
// Get the sums
if(!in_array($cat,$return)){
$sum1 = 0;
$sum2 = 0;
foreach ($datas as $data) {
if($data["category"] == $cat){
$sum1 += $data["value1"];
$sum2 += $data["value2"];
}
}
}
// Push the sums in the return array
array_push($return,[
"name" => $cat,
"value1" => $sum1,
"value2" => $sum2,
]);
// Push the single elements
foreach ($datas as $data) {
if($cat == $data["category"]){
array_push($return,[
"name" => $data["name"],
"value1" => $data["value1"],
"value2" => $data["value2"],
]);
}
}
}
return $return;
}
Here is a PHPFiddle to try it out... Hit [F9] to run.
It is much more direct, efficient, and readable to implement a single loop and push reference variables into the result array to allow summing based on shared categories without keeping track of the actual indexes of the category rows.
Code: (Demo)
$result = [];
foreach ($array as $row) {
if (!isset($ref[$row['category']])) {
$ref[$row['category']] = [
'name' => $row['category'],
'value1' => 0,
'value2' => 0
];
$result[] = &$ref[$row['category']];
}
$ref[$row['category']]['value1'] += $row['value1'];
$ref[$row['category']]['value2'] += $row['value2'];
unset($row['category']);
$result[] = $row;
}
var_export($result);
I usually use Eloquent so transposing the data is much easier. However i'm struggling to this in vanilla PHP.
I have tried array_map(null, ...$array) however get an error due to it not being an array.
I have got the following keyed array:
[
'email' => [
"william.pool#gmail.com",
"martynleeball#gmail.com"
],
'lastName' => [
'Pool',
'Ball'
],
'firstName' => [
'William',
'Martyn'
],
'id' => [
'j8zwyk',
'1'
]
]
I need to convert this to the following format:
[
0 => [
'email' => "william.pool#gmail.com",
'lastName' => 'Pool',
'firstName' => 'William',
'id' => 'j8zwyk'
],
1 => [
'email' => "martynleeball#gmail.com",
'lastName' => 'Ball',
'firstName' => 'Martyn',
'id' => '1'
]
]
Create new array with length 2 and loop through origin array. In loop insert relevant item into new array.
So if your array has only 2 item per key use
$newArr = [];
foreach($arr as $key=>$item){
$newArr[0][$key] = $item[0];
$newArr[1][$key] = $item[1];
}
But if it has unknown item use
$newArr = [];
foreach($arr as $key=>$item){
foreach($item as $key2=>$item2)
$newArr[$key2][$key] = $item2;
}
Check result in demo
$newArray = [];
foreach ($array as $key => $value) {
for ($i = 0; $i < count($value); $i++) {
$newArray[$i][$key] = $value[$i];
}
}