I have an array that need to be validated on the server.
The array look like this:
array:2 [
0 => array:5 [
"sku" => "8x0c"
"price" => "30.00"
"stock" => 5
"option_1" => 2
"option_2" => 5
]
1 => array:5 [
"sku" => "qsaz"
"price" => "30.00"
"stock" => 5
"option_1" => 2
"option_2" => 5
]
]
This array contains only 2 entries (it can contain multiple entries).
option_1, option_2 and option_3 are dynamic fields and need to be unique.
The array can have minimum one dynamic field (option_1) and maximum 3 fields (option_1, option_2 and option_3)
In this case in the first array
"option_1" => 2
"option_2" => 5
and in the second
"option_1" => 2
"option_2" => 5
are the same (i have two variations for a product that have the same color and size - and I don't want this).
How can I validate / check for duplicates for the selected options ?
If the two options keys in the items array are always going to be present, then the below function would allow you to validate the options being unique:
function validateUniqueOptions(array $items): bool
{
// Keep track the options we've seen before
$optionsSeen = [];
foreach ($items as $item) {
// Generate a unique key for this item combining its selection options
$optionKey = $item['option_1'] . '_' . $item['option_2'];
if (in_array($optionKey, $optionsSeen)) {
// This item is a duplicate of another we've seen previously, validation failed
return false;
}
$optionsSeen[] = $optionKey;
}
// No duplicates found, validation successful
return true;
}
This function below could handle as many option you'd like and if there are three options and two options in the other it is unique as well.
function check_options_duplicate($arr) {
$options_arr = ["","",""]; //Place for three options..
foreach($arr as $topkey=>$toplevel) {
foreach($toplevel as $key=>$item) {
if (substr($key,0,7) == 'option_') {
$options_arr[$topkey] .= $item . ",";
}
}
}
//If number of items in array are not the same when removing
//duplicate values with array_unique => it is a duplicate
if (count($options_arr) !== count(array_unique($options_arr))) {
return true;
}
return false;
}
Usage:
$is_duplicate = check_options_duplicate($arr);
if ($is_duplicate) {
echo 'is duplicate';
}
else {
echo 'is not duplicate';
}
Example: If having the array $arr:
Array
(
[0] => Array
(
[sku] => 8x0c
[price] => 30.00
[stock] => 5
[option_1] => 4
[option_2] => 3
)
[1] => Array
(
[sku] => qsaz
[price] => 30.00
[stock] => 5
[option_1] => 4
[option_2] => 33
)
)
it would result in the $options_arr array
Array
(
[0] => 4,3,
[1] => 4,33,
[2] =>
)
If doing an array_unique() it would be the same:
Array
(
[0] => 4,3,
[1] => 4,33,
[2] =>
)
If it both options would be the same:
Array
(
[0] => 4,3,
[1] => 4,3,
[2] =>
)
and you're doing an unique_array() it would only contain two items instead of 3.
Therefore it's a duplicate when you compare original $options_arr with unique $options_arr.
Array
(
[0] => 4,3,
[2] =>
)
Related
This question already has answers here:
How to access and manipulate multi-dimensional array by key names / path?
(10 answers)
Closed 1 year ago.
I'm trying to create a function that will assign new values to a specific indexes in a multidimensional array:
I Have the array that looks like this:
data[i]['checkin'];
data[i]['checkout'];
data[i]['content'][x]['price'];
data[i]['content'][x]['profit'];
data[i]['content'][x]['exchangerate'];
first parameter of my function will get the array, And second parameter will get the indexes that I want to redefine:
For example:
function defineNewValues(&$arr, $keys) {
//logic
}
Call the function:
defineNewValues($myArray, [
'data.*.content.*.price' => 0,
'data.*.content.*.profit => 0,
]);
Im beliving that recursion is the key for my problem ,
But not really know how to solve it.
Thank You.
could something like this be okay?
I only ask you to study this code not to implement it, for the simple reason that in the future you may have the same type of problem.
function setValue($key,$value,&$array){
$find_parts = explode(".", $key);
$find = $find_parts[0]??null;
if ($find!=null){
if ($find == "*"){
array_shift($find_parts);
foreach($array as &$sub_array){
setValue(implode(".",$find_parts),$value,$sub_array);
}
}else{
if (count($find_parts)>1){
if (array_key_exists($find,$array)){
array_shift($find_parts);
setValue(implode(".",$find_parts),$value,$array[$find]);
}
}else{
if (array_key_exists($find,$array)){
$array[$find] = $value;
}
}
}
}
}
function defineNewValues(&$arr, $keys) {
foreach($keys as $key=>$value){
setValue($key,$value,$arr);
}
}
$myArray=[
"data"=>[
"a"=>[
"content"=>[
"aa"=>[
"price" => 3,
"profit" => 2,
"other" => 1
],
"ab"=>[
"price" => 3,
"profit" => 2,
"other" => 2
]
]
],
"b"=>[
"content"=>[
"ba"=>[
"price" => 3,
"profit" => 2,
"other" => 4
],
"bb"=>[
"price" => 3,
"profit" => 2,
"other" => 5
]
]
],
]
];
defineNewValues($myArray, [
"data.*.content.*.price" => 0,
"data.*.content.*.profit" => 0,
]);
print_r($myArray);
/* OUTPUT
Array
(
[data] => Array
(
[a] => Array
(
[content] => Array
(
[aa] => Array
(
[price] => 0
[profit] => 0
[other] => 1
)
[ab] => Array
(
[price] => 0
[profit] => 0
[other] => 2
)
)
)
[b] => Array
(
[content] => Array
(
[ba] => Array
(
[price] => 0
[profit] => 0
[other] => 4
)
[bb] => Array
(
[price] => 0
[profit] => 0
[other] => 5
)
)
)
)
)
*/
Because the keys you want to replace only occur at one level of the data, the solution doesn't really need to take the entire array structure into account. You can just replace every price and profit key.
array_walk_recursive($example, function(&$value, $key) {
if (in_array($key, ['price', 'profit'])) {
$value = 0;
}
});
Based on your comment on the other answer, my opinion on the "correct and professional way" is that we should try to solve the problem in the simplest way possible, because simple solutions are easy to maintain.
I've got a database table that looks like this:
uid | group | category
1 | group1 | cat1
2 | group1 | cat2
3 | group2 | cat3
4 | group2 | cat4
5 | group2 | cat5
6 | group3 | cat6
7 | group3 | cat7
But I need this data in an array, that groups categories by their group.
For example, my array should look like this:
Array
(
[group1] => Array
(
[0] => Array
(
[0] => 1
[1] => cat1
)
[1] => Array
(
[0] => 2
[1] => cat2
)
)
[group2] => Array
(
[0] => Array
(
[0] => 3
[1] => cat3
)
[1] => Array
(
[0] => 4
[1] => cat4
)
[2] => Array
(
[0] => 5
[1] => cat5
)
)
[group3] => Array
(
[0] => Array
(
[0] => 6
[1] => cat6
)
[1] => Array
(
[0] => 7
[1] => cat7
)
)
)
I've written a foreach loop that does just this, but I have a problem.
My problem is that it always leaves out the very last row of the table, and I'm not sure how to fix it. In my mind, the logic dictates that it should always work.
I was thinking that after the loop I could just add the very last row to the new array, but I think that may cause issues if the last row has a different group, and I would rather the solution be built into the foreach loop.
Unfortunately, I am at a loss here. How can I fix my code to include the very last row of the database query?
I would also be interested to see what improvements I can make on my current code, but that may be a better question for codereview.
My loop:
$pass = [];
foreach($stmt as $key => $value) {
if(empty($currentGroup)) $currentGroup = $value['group'];
if(empty($temp)) $temp = [];
if($currentGroup != $value['group'] || $key+1 == count($stmt)) {
$pass[$currentGroup] = $temp;
$currentGroup = $value['group'];
$temp = [];
$temp[] = [$stmt[$key]['uid'], $stmt[$key]['category']];
} else {
$temp[] = [$stmt[$key]['uid'], $stmt[$key]['category']];
}
}
The following should do it:
<?php
//Create an array to store our grouped rows
$grouped = array();
//Loop over all rows returned by the $stmt that has been executed.
//You could probably remove the key from here, it's not needed it seems.
//The keys within the $value array will match the names of the columns in
//the database,
foreach($stmt as $key => $value){
//As we're storing by the group value from the row we first want to
//check if our grouped array contains a key for the group of the row
//being processed. If it does not, create an empty array within the
//grouped data for this group.
if(!array_key_exists($value['group'], $grouped)){
$grouped[$value['group']] = array();
}
//Knowing we will always have an array element for the rows group
//we can blindly append the values for this row to the grouped
//container using its values.
//'[] =' is just short hand append.
$grouped[$value['group']][] = array(
$value['uid'],
$value['category']
);
}
Hope that helps!
To further future proof this loop you could change the grouped value append to the following:
<?php
//Setting the whole row (minus the group) rather than just the uid
//and category explicitly allows this code to work without modification
//as the datatable changes, ie. new columns. Assuming that is the 'group'
//column remains present
unset($value['group']);
$grouped[$value['group']][] = $value;
Grouped contents data could now be accessed using the following:
<?php
//Acceess data via column name not array index, yay!
echo $grouped['group1']['uid']
I've needed this again recently, so I made a function based around #JParkinson1991's answer.
I'm putting this here for documentation, and to possibly help future readers.
function groupArray($arr, $group, $preserveSubArrays = false, $preserveGroupKey = false) {
$temp = array();
foreach($arr as $key => $value) {
$groupValue = $value[$group];
if(!$preserveGroupKey)
{
unset($arr[$key][$group]);
}
if(!array_key_exists($groupValue, $temp)) {
$temp[$groupValue] = array();
}
if(!$preserveSubArrays){
$data = count($arr[$key]) == 1? array_pop($arr[$key]) : $arr[$key];
} else {
$data = $arr[$key];
}
$temp[$groupValue][] = $data;
}
return $temp;
}
Breakdown
function groupArray($arr, $group, $preserveGroupKey = false, $preserveSubArrays = false)
This function accepts 2 to 4 parameters.
The flat array you want to group (array)
The key you want to group by (string/int)
Option to preserve the group key in the output of each sub array (Boolean)
Option to preserve sub arrays. If only 1 key exists in each sub array, the function will store just the single value for each row instead of an array (Boolean)
The first parameter is the array itself, the second parameter is the key that you want to group by, and the 3rd (optional) parameter is a boolean that tells the function if you want to preserve the group key in the sub arrays.
$temp = array();
foreach($arr as $key => $value) {
$groupValue = $value[$group];
if(!$preserveGroupKey)
{
unset($arr[$key][$group]);
}
if(!array_key_exists($groupValue, $temp)) {
$temp[$groupValue] = array();
}
$temp[$groupValue][] = $arr[$key];
}
First, we create a temporary array called $temp
Next, we loop through the array grabbing the key (Which should be a string or int), and the value (Which should be an array).
We set $groupValue to whatever the value is of the $group you chose, such as "group" in the example below.
$arr = [
0 => [
"group" => "group1",
"name" => "Bob",
],
1 => [
"group" => "group1",
"name" => "Randy",
],
2 => [
"group" => "group1",
"name" => "Susan",
],
3 => [
"group" => "group2",
"name" => "Larry",
],
4 => [
"group" => "group2",
"name" => "David",
],
5 => [
"group" => "group3",
"name" => "Perry",
],
];
Then we check if we want to $preserveGroupKey's. If this boolean is false (And it is by default), the key will be removed leaving several subarrays with just the "name" key left.
Now we check if the $groupValue exists in our $temp array, if it does not, we create it.
Then we add to the $temp[$groupValue] whatever the current row values are. From the example above, we would end up with:
Array
(
[group1] => Array
(
[0] => Bob
[1] => Randy
[2] => Susan
)
[group2] => Array
(
[0] => Larry
[1] => David
)
[group3] => Array
(
[0] => Perry
)
)
Or, with the 3rd parameter set to true you would get:
Array
(
[group1] => Array
(
[0] => Array
(
[name] => Bob
)
[1] => Array
(
[name] => Randy
)
[2] => Array
(
[name] => Susan
)
)
[group2] => Array
(
[0] => Array
(
[name] => Larry
)
[1] => Array
(
[name] => David
)
)
[group3] => Array
(
[0] => Array
(
[name] => Perry
)
)
)
I have an array coming from my database and simply it consists of questions and answers. I am trying to merge 2 arrays and create multidimensional array if values are more than one.
Array
(
[0] => Array
(
[question_id] => 1
[option_id] => 1
)
[1] => Array
(
[question_id] => 2
[option_id] => 3
)
[2] => Array
(
[question_id] => 3
[option_id] => 5
)
[3] => Array
(
[question_id] => 3
[option_id] => 6
)
)
I've tried to separate answers and questions to 2 different arrays but couldn't figure how to merge them again.
$user_questions = array_column($answers, 'question_id');
$user_answers = array_column($answers, 'option_id');
What I need is (question 3 has 2 answers) :
Array
(
[1] => 1
[2] => 3
[3] => Array (5, 6)
)
You can group your data like this as you fetch the results from your query instead of processing it after the fact. To get the array you have now, you're currently doing something like this:
while ($row = $stmt->someFetchMethod()) {
$result[] = $row;
}
Instead, use the question id as the key in your result array, and append the option id to an array at that key.
while ($row = $stmt->someFetchMethod()) {
$result[$row['question_id']][] = $row['option_id'];
}
Below code will create a new array by looping the existing array.
// Considering your existing array to be like this
$array = array(
'0' => array('question_id' => 1,'option_id' => 1),
'1' => array('question_id' => 2, 'option_id' => 3 ),
'2' => array('question_id' => 3,'option_id' => 5),
'3' => array('question_id' => 3,'option_id' => 6)
);
//define new array
$new_array = array();
// loop the array
foreach($array as $key=>$value){
// if the option/answer is already set to to question key
if(isset($new_array[$value['question_id']])){
// if question key is an array, push new option to that array
if(is_array($new_array[$value['question_id']])){
array_push($new_array[$value['question_id']], $value['option_id']);
}else{
// convert question key to array with the old value and new option value
$new_array[$value['question_id']] = array($new_array[$value['question_id']],$value['option_id']);
}
}
else{
// assing option as value to question key
$new_array[$value['question_id']] = $value['option_id'];
}
}
print_r($new_array);
Out put:
Array
(
[1] => 1
[2] => 3
[3] => Array
(
[0] => 5
[1] => 6
)
)
I have multi-dimentional array like below,
$product = array(
"2e471a22b1b994a7cb3f3a40cee9fba2" => array (
"product" => 6004,
"unique_key" => 3a8a5cb029ee3b92cfc90de23e2329ab,
"product_id" => 51,
"line_total"=>99,
"quantity"=>1,
"data"=> array(
"id"=> 51,
"post"=>array(
"ID"=>51,
"post_title"=>"Prodcut four - control",
),
"price"=>99
)
),
"a7d0f813832ec8a2bf24269ff7145d0c" => array (
"product" => 6004,
"unique_key" => c30d1ca26d30aa3dc3c9aa04f0b585ce,
"product_id" => 51,
"line_total"=>99,
"quantity"=>1,
"data"=> array(
"id"=> 51,
"post"=>array(
"ID"=>51,
"post_title"=>"Prodcut four - control",
),
"price"=>99
)
)
);
Need to remove the duplicate values based on 'product_id' array value and increase the quantity values based on number of products.
Note: The above array have 'unique key' too so any single unique key is needed in array result.
Expected Result should be:
$resultproduct = array(
"2e471a22b1b994a7cb3f3a40cee9fba2" => array (
"product" => 6004,
"unique_key" => 3a8a5cb029ee3b92cfc90de23e2329ab,
"product_id" => 51,
"line_total"=>99,
"quantity"=>2,
"data"=> array(
"id"=> 51,
"post"=>array(
"ID"=>51,
"post_title"=>"Prodcut four - control",
),
"price"=>99
)
));
Working code at eval.in
I try and make the code easy to understand so more variables and lines of code than is absolutely required.
Explanation:
1) Need to use the one of original product array index as the output table key e.g. "2e471a22b1b994a7cb3f3a40cee9fba2" for product 51.
2) It needs to be fast relating input productId to the output key. So, I used a lookup table ProductIdList that matches productId to output key.
It is then a two stage lookup to find the entry in the output and add quantities to it.
The code:
// create a product_id => first key table
$productIdList = array();
// output...
$productTotal = array();
foreach ($product as $origIndex => $entry) {
$curProductId = $entry['product_id'];
// check product_id exists in the lookup...
if (isset($productIdList[$curProductId])) { // add to the total...
$productTotalIndex = $productIdList[$curProductId];
$productTotal[$productTotalIndex]['quantity'] += $entry['quantity'];
}
else { // add the entry to the output and the productIdList...
$productIdList[$curProductId] = $origIndex;
$productTotal[$origIndex] = $entry;
}
}
Output: The totals array:
Array
(
[2e471a22b1b994a7cb3f3a40cee9fba2] => Array
(
[product] => 6004
[unique_key] => 3a8a5cb029ee3b92cfc90de23e2329ab
[product_id] => 51
[line_total] => 99
[quantity] => 2
[data] => Array
(
[id] => 51
[post] => Array
(
[ID] => 51
[post_title] => Prodcut four - control
)
[price] => 99
)
)
[test02] => Array
(
[product] => 6664
[unique_key] => c30d1ca26d30aa3dc3c9aa04f0b585ce
[product_id] => 666
[line_total] => 99
[quantity] => 579
[data] => Array
(
[id] => 666
[post] => Array
(
[ID] => 666
[post_title] => Prodcut 666 - control
)
[price] => 99
)
)
)
The productId to original key list:
array (size=2)
51 => string '2e471a22b1b994a7cb3f3a40cee9fba2' (length=32)
666 => string 'test02' (length=6)
You need to loop over each product and use the product_id as the key for a new array. This adds the quantities as it goes, so should work for any quantities other than 1.
$result = [];
foreach ($product as $p)
{
if (isset($result[$p['product_id']]))
{
$result[$p['product_id']]['quantity']+= $p['quantity'];
}
else
{
$result[$p['product_id']] = $p;
}
}
print_r($result);
I have the following multidimensional array:
Array ( [0] => Array
( [id] => 1
[name] => Jonah
[points] => 27 )
[1] => Array
( [id] => 2
[name] => Mark
[points] => 34 )
)
I'm currently using a foreach loop to extract the values from the array:
foreach ($result as $key => $sub)
{
...
}
But I was wondering how do I see whether a value within the array already exists.
So for example if I wanted to add another set to the array, but the id is 1 (so the person is Jonah) and their score is 5, can I add the 5 to the already created array value in id 0 instead of creating a new array value?
So after the loop has finished the array will look like this:
Array ( [0] => Array
( [id] => 1
[name] => Jonah
[points] => 32 )
[1] => Array
( [id] => 2
[name] => Mark
[points] => 34 )
)
What about looping over your array, checking for each item if it's id is the one you're looking for ?
$found = false;
foreach ($your_array as $key => $data) {
if ($data['id'] == $the_id_youre_lloking_for) {
// The item has been found => add the new points to the existing ones
$data['points'] += $the_number_of_points;
$found = true;
break; // no need to loop anymore, as we have found the item => exit the loop
}
}
if ($found === false) {
// The id you were looking for has not been found,
// which means the corresponding item is not already present in your array
// => Add a new item to the array
}
you can first store the array with index equal to the id.
for example :
$arr =Array ( [0] => Array
( [id] => 1
[name] => Jonah
[points] => 27 )
[1] => Array
( [id] => 2
[name] => Mark
[points] => 34 )
);
$new = array();
foreach($arr as $value){
$new[$value['id']] = $value;
}
//So now you can check the array $new for if the key exists already
if(array_key_exists(1, $new)){
$new[1]['points'] = 32;
}
Even though the question is answered, I wanted to post my answer. Might come handy to future viewers. You can create new array from this array with filter then from there you can check if value exist on that array or not. You can follow below code. Sample
$arr = array(
0 =>array(
"id"=> 1,
"name"=> "Bangladesh",
"action"=> "27"
),
1 =>array(
"id"=> 2,
"name"=> "Entertainment",
"action"=> "34"
)
);
$new = array();
foreach($arr as $value){
$new[$value['id']] = $value;
}
if(array_key_exists(1, $new)){
echo $new[1]['id'];
}
else {
echo "aaa";
}
//print_r($new);