I'm stuck with a tricky task I can't solve without the code being very messy with a lot of foreach loops.
This is the fundamental logic:
There can only be unique foods in the array, so only one guy can have Pizza, only one guy can have Pasta etc. But they can have multiple foods. And the guy with the most foods in the array, should win the foods over the ones with few foods.
So John should be deleted from the array since either Joe or Conny will have his.
And since Joe and Conny have the same amount of foods, one of them will keep all of his foods and the other one should be deleted from the array and it should be picked randomly whether it is Joe or Conny.
The starting array looks like this:
Array
(
[Joe] => Array
(
[0] => Pizza
[1] => Pasta
[2] => Lasagne
[3] => Tacos
[4] => Hamburger
)
[Conny] => Array
(
[0] => Pizza
[1] => Pasta
[2] => Lasagne
[3] => Tacos
[4] => Hamburger
)
[John] => Array
(
[0] => Pizza
[1] => Pasta
)
[Fred] => Array
(
[0] => Pizza
[1] => Pasta
[2] => Soup
[3] => Fish
)
[Greg] => Array
(
[0] => Sushi
)
)
And the end result I want is:
Array
(
[Joe] => Array
(
[0] => Pizza
[1] => Pasta
[2] => Lasagne
[3] => Tacos
[4] => Hamburger
)
[Fred] => Array
(
[2] => Soup
[3] => Fish
)
[Greg] => Array
(
[0] => Sushi
)
)
So only unique foods left in the array and if two or more guys have the same amount, it should randomly pick whether of these should keep the foods.
Any suggestions how I can do this?
You have to first sort the array depending on the count of elements in the subarray in reverse order using an user-defined function with uasort. Then you can use two foreach loops to loop through the loop. You will use a helper variable to determine if a food has already been used and if so unset the element. After doing that you filter out the empty elements in the array with array_filter.
uasort($food, function($a, $b) {
return count($b) <=> count($a);
});
$used = array();
foreach($food as $kl => $pl) {
foreach($pl as $k => $p) {
if(!in_array($p, $used)) {
$used[] = $p;
} else {
unset($food[$kl][$k]);
}
}
}
$food = array_filter($food);
You can see it here: https://3v4l.org/LjpGD
Reference:
http://php.net/uasort
http://php.net/in_array
http://php.net/array_filter
PS: The code uses the new since PHP 7.0 available spaceship operator. If you don't or can't use PHP7, you have to use an if construct as below for comparison.
if(count($b) > count($a)) {
return 1;
} elseif(count($b) == count($a)) {
return 0;
} else {
return -1;
}
Related
I'm consuming an API which returns an array of objects as this:
$base = array(
["orange","_","banana"],
["banana","_","_"],
["_","apple","kiwi"],
["_","raspberry","strawberry"]
);
And I intend to show "0" when key value is "_" however I haven't found a better way to do this than this:
foreach ($base as $key => $value) {
for ($i=0; $i<=3;$i++) {
if ($base[$key][$i]=="_")
$base[$key][$i]="0";
}
}
This works just fine since it's a simple demo but the real array is sometimes big and I've found this solution somewhat inefficient.
My question is, there's some php built-in function to do achieve this in or at least a better way to do this?
Thanks in advance guys,
Use array_walk_recursive(), pass the elements by reference and walk over the array, checking for the value _ - if its a match, replace it with 0.
$base = array(
["orange","_","banana"],
["banana","_","_"],
["_","apple","kiwi"],
["_","raspberry","strawberry"]
);
array_walk_recursive($base, function(&$v) {
if ($v === '_')
$v = 0;
});
Output becomes
Array
(
[0] => Array
(
[0] => orange
[1] => 0
[2] => banana
)
[1] => Array
(
[0] => banana
[1] => 0
[2] => 0
)
[2] => Array
(
[0] => 0
[1] => apple
[2] => kiwi
)
[3] => Array
(
[0] => 0
[1] => raspberry
[2] => strawberry
)
)
Live demo at https://3v4l.org/6Bs8ZE
You can replace _ with 0;
json_decode(str_replace('"_"','"0"',json_encode($base)));
If I have an array like the one below how would I go about moving key [2] and its associated value to the beginning of the array ? (making it key [0] and increasing the other keys by 1)
Current:
[0] => Array
(
[name] => Vanilla Coke cans 355ml x 24
)
[1] => Array
(
[name] => Big Red Soda x 24
)
[2] => Array
(
[name] => Reeses White PB Cups - 24 CT
)
Desired outcome:
[0] => Array
(
[name] => Reeses White PB Cups - 24 CT
)
[1] => Array
(
[name] => Vanilla Coke cans 355ml x 24
)
[2] => Array
(
[name] => Big Red Soda x 24
)
EDIT
To clarify I do will always want to move an element to the begining of the array but it wont necessarily be the last element it can sometimes be the 3rd 4th e.c.t. it varies each time.
array_splice removes (and optionally replaces / inserts) values from an array returning an array with the removed items. In conjunction with the simple array_unshift function the job could be done.
$arr = [1,2,3,4,5];
function array_move_item_as_first(array $array, int $idx) : array
{
array_unshift( $array, array_splice($array, $idx, 1)[0] );
return $array;
}
print_r(array_move_item_as_first($arr, 2));
output:
Array
(
[0] => 3
[1] => 1
[2] => 2
[3] => 4
[4] => 5
)
Why don't you use array_unshift and array_pop together?
array_unshift($someArray, array_pop($someArray));
array_pop removes the last element, and array_shift prepends the entry to the array.
I'm having a real headache trying to iterate through an array and output elements. Using the array structure below I want to be able to output each instance of partname.
The following loop outputs the first instance of partname. I can't seem to adapt it to loop through all instances within the array. I'm sure I'm missing something basic.
foreach($ItemsArray['assignments'] as $item) {
$partname = $item['grades'][0]['partname'];
}
Array
(
[assignments] => Array
(
[0] => Array
(
[assigntmentid] => 5101
[grades] => Array
(
[0] => Array
(
[id] => 5101
[name] => Advanced AutoCad
[partid] => 6601
[partname] => Draft
[userid] => 82069
[grade] => 53
[courseid] => 6265
[fullname] => Computer Aided Design
)
)
)
[1] => Array
(
[assigntmentid] => 5101
[grades] => Array
(
[0] => Array
(
[id] => 5101
[name] => Advanced AutoCad
[partid] => 6602
[partname] => Final
[userid] => 82069
[grade] => 35
[courseid] => 6265
[fullname] => Computer Aided Design
)
)
)
)
)
Instead of just coding by slapping the keyboard. Write down what your function needs to do. In english (or whatever language you prefer). This would be something like:
Foreach assignment, loop over all grades and store the partname of
that grade into an array.
And then code it:
function getPartnames($assignments) {
$partNames = array();
foreach ($assignments as $assignment) {
foreach($assignment['grades'] as $grade) {
$partNames[] = $grade['partname'];
}
}
return $partNames;
}
So what did I do? I simply translated english to code.
Some few more tips: Use variables names that make sense. $item; $ItemArray; ... don't make sense. They tell me nothing
use an extra foreach in your loop:
foreach($ItemsArray['assignments'] as $item) {
foreach($item['grades'] as $grade) {
echo $grade['partname'];
}
}
My sql table consists of ID,Book name,Genre and ratings.Currently my array from my sql table looks like this (output)
Array (
[book] => Array (
[0] => Array
( [0] => 1
[1] => book one
[2] => Fantasy/Horror
[4] => 8.9 )
[1] => Array (
[0] => 2
[1] => book two
[2] => Comedy
[4] => 8.3 ) ) )
i want to sort this array to DESC by ID
Also in the future by title.
The array variable is $book so i wrote ksort($book) also tried arsort
for some reason it doesnt work? Is it because of the array? Thanks!
IF you are able to:
Use SQL sorting instead, because of the speed and flexibility (Use db-engine where it's appropiate) Only use array-functionality if you really need to! :-)
Do something like:
SELECT ID,Book name,Genre, ratings ORDER BY ID DESC
OR
If you have still have to use array sorting, use usort() in PHP:
<?php
//Book array
$bookArray['book'][0] = array(2, 'book one', 'Fantasy/Horror', 8.9);
$bookArray['book'][1] = array(1, 'book two ', 'Comedy', 8.3);
function sortByIndex($a, $b) {
return $a[0] - $b[0];
}
usort($bookArray['book'], 'sortByIndex');
echo 'RESULT=' . print_r($bookArray,true);
?>
Results in this output:
RESULT=Array ( [book] => Array ( [0] => Array ( [0] => 1 [1] => book two [2] => Comedy [3] => 8.3 ) [1] => Array ( [0] => 2 [1] => book one [2] => Fantasy/Horror [3] => 8.9 ) ) )
Book two comes first here, because in my example I set the first index to be 1 with value 'book two'.
The easiest way is to add this to your SQL statement at the end:
ORDER BY id DESC
Also if you really want to know how to do it in PHP you would do it this way:
function cmp($a, $b)
{
if ($a[0] == $b[0]) {
return 0;
}
return ($a[0] > $b[0]) ? -1 : 1;
}
usort($array['book'], "cmp");
Just you need to change your Select Statement. It should be something like this -
select id,bookname,genre,ratings from yourtable ORDER BY id DESC,bookname;
Edit: Thanks to #Felix Kling and #mario for pointing me towards named capture groups and PREG_SET_ORDER, I totally learned something today.
I'm curious about a better algorithm per se, though. So please just pretend that there's no preg_match() involved.
Edit 2: Abstracted question
While answering another question here, I stumbled upon the fact that my code for turning
this:
Array
(
[0] => Array (
[0] => 1
[1] => 3
)
[1] => Array (
[0] => Description text
[1] => Different Description text
)
[2] => Array (
[0] => 123.456.12
[1] => 234.567.89
)
[3] => Array (
[0] => 10.00
[1] => 10.00
)
[4] => Array (
[0] => 10.00
[1] => 30.00
)
)
into that:
Array
(
[0] => Array
(
[qty] => 1
[description] => "Description text"
[sku] => 123.456.12
[price] => 10.00
[total] => 10.00
)
…
)
is fugly:
$field_names = array('qty', 'description', 'sku', 'price', 'total');
$result_arr = array();
$num_iter = count(matches[0]);
for ($i = 0; $i < $num_iter; $i++) {
foreach ($field_names as $index => $field_name) {
$result_arr[$i][$field_name] = array_shift($input_arr[$index]);
}
}
Any suggestions for improvement?
There is one simpler way to produce the desired output.
while (count($input_arr[0])) {
$values = array_map("array_shift", & $input_arr);
$result_arr[] = array_combine($field_names, $values);
}
This won't work past PHP 5.3, as it requires forcibly passing a parameter by reference. (Avoiding any dumbing-down-the-language remarks here). But you can of course chop off the entries with a more elaborate manual loop at any time.
The real simplification for such cases is however array_combine to turn a list into an associative array.