I have an array which has different items example shirts, shoes, ties, jackets etc. And each of these items can have multiple designs denoted by their ids.
$variations = array(
'shirts' => array('2'),
'shoes' => array('7', '3'),
'jackets' => array('1', '5')
);
Now we are looking for an efficient way to create different variations of all of these.
## Required result ##
$result = array(
array('2','7','1'),
array('2', '7', '5'),
array('2','3','1'),
array('2','3','5')
);
Any help would be appreciated :)
EDIT: Our current function
function cartesian($input) {
$result = array();
while (list($key, $values) = each($input)) {
// If a sub-array is empty, it doesn't affect the cartesian product
if (empty($values)) {
continue;
}
// Seeding the product array with the values from the first sub-array
if (empty($result)) {
foreach($values as $value) {
$result[] = array($key => $value);
}
}
else {
// Second and subsequent input sub-arrays work like this:
// 1. In each existing array inside $product, add an item with
// key == $key and value == first item in input sub-array
// 2. Then, for each remaining item in current input sub-array,
// add a copy of each existing array inside $product with
// key == $key and value == first item of input sub-array
// Store all items to be added to $product here; adding them
// inside the foreach will result in an infinite loop
$append = array();
foreach($result as &$product) {
// Do step 1 above. array_shift is not the most efficient, but
// it allows us to iterate over the rest of the items with a
// simple foreach, making the code short and easy to read.
$product[$key] = array_shift($values);
// $product is by reference (that's why the key we added above
// will appear in the end result), so make a copy of it here
$copy = $product;
// Do step 2 above.
foreach($values as $item) {
$copy[$key] = $item;
$append[] = $copy;
}
// Undo the side effecst of array_shift
array_unshift($values, $product[$key]);
}
// Out of the foreach, we can add to $results now
$result = array_merge($result, $append);
}
}
return $result;
}
While I agree with comments beneath your question I implemented generator-based solution for fun so I can share it anyway:
$variations = array(
'shirts' => array('2'),
'shoes' => array('7', '3'),
'jackets' => array('1', '5')
);
var_dump(iterator_to_array(cartesian($variations), false));
function cartesian($lists, $product = [])
{
if (empty($product)) {
// first run, reverse array for array_pop and remove empty lists
$lists = array_reverse(array_filter($lists, 'count'));
}
$curr = array_pop($lists);
foreach ($curr as $c) {
if (empty($lists)) {
yield array_merge($product, [$c]);
} else {
yield from cartesian($lists, array_merge($product, [$c]));
}
}
}
This is how I did it
$parameters = array(
'shirts' => array('2'),
'shoes' => array('7', '3'),
'jackets' => array('1', '5')
);
$arPhrases = $parameters[0];
for ($i = 1; $i < count($parameters); $i++) {
$notFullCount = count($arPhrases);
foreach ($arPhrases as $phrase) {
foreach ($parameters[$i] as $newPart) {
$arPhrases[] = $phrase." ".$newPart;
}
}
$arPhrases = array_slice($arPhrases, $notFullCount);
}
Related
I have array of objects, for each objects i have name, category, price and couple other things. and from that array i want to remove objects that has specific category.
public function getCSV()
{
$contents = Storage::disk('dropbox')->get('InventoryReport.csv');
$lines = explode(PHP_EOL, $contents);
$items = array();
// categories to ignore when importing
$ignore = ['Misc', 'Soft Drinks', 'Juices', 'Water', 'Snack', 'Energy Drink', 'Tobacco', 'Gum', 'Account Payment'];
foreach ($lines as $line)
{
$items[] = str_getcsv($line);
}
array_shift($items);
array_pop($items);
// foreach ($items as $item)
// {
// $i = Item::where('upc', $item[7])->first();
// if($i == null)
// {
// $name = str_slug($item[8], '-');
// // $inventory = Item::create(
// // ['upc' => $item[7],
// // 'name' => $item[8],
// // 'price' => $item[9],
// // 'stock' => $item[10],
// // 'cost' => $item[11],
// // 'category' => $item[2],
// // 'slug' => $name
// // ]
// // );
// }
// }
}
above is my code. I want to remove all the items in array that has category thats inside $ignore array. before i store it into database.
Replace this:
foreach ($lines as $line)
{
$items[] = str_getcsv($line);
}
with:
foreach ($lines as $line) {
$item = str_getcsv($line);
if (count($item) > 1 && !in_array($item[2], $ignore)) {
$items[] = $item;
}
}
Now $items will only have the items you are looking for.
NB: Review whether the calls of array_shift and array_pop are still needed, depending on what the category was of those two items.
i will ask how to insert array if amount sub arrays not same, i have problem if the $arrays != 3 because in sub arrays 2 in arrays one
<?php
$arrays=array(
array('a','b'),
array('a','b','c'),
array('a','b','c')
);
$per=0;
for ($h=0; $h < count($arrays); $h++) {
if (count($arrays) > $per) {
$per= count($arrays);
}
}
$koneksi = new mysqli("127.0.0.1","root","","data");
$nil=array();
foreach ($arrays as $data) {
foreach ($data as $key => $value) {
$data[$key] = $data[$key];
}
$nil[]="('".implode("', '",$data). "')";
}
$insert="INSERT INTO datams (data1,data2,data3) VALUES ".implode(', ',$nil);
$queri=mysqli_query($koneksi, $insert);
if ($queri == true){
echo 'upload done'.PHP_EOL;
} else {
echo 'fail upload'.PHP_EOL;
}
i can't insert the data if sub array same, can help me ?
You will have to make the arrays the same length.
Create an empty array, then loop your existing array, adding the missing values using array_replace. After that, you can insert them.
$empty=['' , '' , ''];
foreach($array as $key => $tmp){
$array[$key] = array_replace($empty,$tmp);
// This replaces the values of $empty with those of $array that are set:
// ['','',''] replace wihh ['a','b','c'] gives ['a','b','c']
// ['','',''] replace with ['a','b'] gives ['a','b','']
}
You will end up with:
$arrays=array(
array('a','b','' ),
array('a','b','c'),
array('a','b','c')
);
Now you can insert the values;
EDIT
To make it more flexible (i.e the original array keeps changing), you first have to find the length of the longest array in $array.
$max=0;
foreach($array as $a) {
$c = count($a);
if( $c > $max) $max = $c;
}
$empty=array_fill(0,$max,'');
I got an array like this:
$array[0][name] = "Axel";
$array[0][car] = "Pratzner";
$array[0][color] = "black";
$array[1][name] = "John";
$array[1][car] = "BMW";
$array[1][color] = "black";
$array[2][name] = "Peggy";
$array[2][car] = "VW";
$array[2][color] = "white";
I would like to do something like "get all names WHERE car = bmw AND color = white"
Could anyone give advice on how the PHP spell would look like?
function getWhiteBMWs($array) {
$result = array();
foreach ($array as $entry) {
if ($entry['car'] == 'bmw' && $entry['color'] == 'white')
$result[] = $entry;
}
return $result;
}
Edited: This is a more general solution:
// Filter an array using the given filter array
function multiFilter($array, $filters) {
$result = $array;
// Removes entries that don't pass the filter
$fn = function($entry, $index, $filter) {
$key = $filter['key'];
$value = $filter['value'];
$result = &$filter['array'];
if ($entry[$key] != $value)
unset($result[$index]);
};
foreach ($filters as $key => $value) {
// Pack the filter data to be passed into array_walk
$filter = array('key' => $key, 'value' => $value, 'array' => &$result);
// For every entry, run the function $fn and pass in the filter data
array_walk($result, $fn, $filter);
}
return array_values($result);
}
// Build a filter array - an entry passes this filter if every
// key in this array corresponds to the same value in the entry.
$filter = array('car' => 'BMW', 'color' => 'white');
// multiFilter searches $array, returning a result array that contains
// only the entries that pass the filter. In this case, only entries
// where $entry['car'] = 'BMW' AND $entry['color'] = 'white' will be
// returned.
$whiteBMWs = multiFilter($array, $filter);
Doing this in code is more or less emulating what a RDBMS is perfect for. Something like this would work:
function getNamesByCarAndColor($array,$color,$car) {
$matches = array();
foreach ($array as $entry) {
if($entry["color"]== $color && $entry["car"]==$car)
matches[] = $entry["name"];
}
return $matches;
}
This code would work well for smaller arrays, but as they got larger and larger it would be obvious that this isn't a great solution and an indexed solution would be much cleaner.
I am developing a user driven eCommerce website and need some help. What I have is a function that will loop through an array remove duplicates and how many times they occur. I then need to run a function on each of those extracted duplicates as many times as they occur. The code I have so far works, but breaks when there are multiple duplicates with the same repetition count. Here is the code I have made so far..
$affiliates = array(11,11,12,12,13,13,13,14,14,14,14); //breaks the code
$affiliates = array(11,11,13,13,13,14,14,14,14,12,12,12,12,12); // works fine
$array = array();
$match_count = array();
foreach($affiliates as $key => $affiliate) {
$array[] = $affiliate;
}
arsort($array); // keeps array index in order
foreach($array as $arrays) {
if(array_value_count($arrays,$array) > 1) {
$match_count[] = array_value_count($arrays,$array);
}
}
$match_count = array_unique($match_count);
$array_unique = arrayDuplicate($array);
$final_array = array_combine($match_count,$array_unique);
foreach($final_array as $key => $value) {
for($i = 0; $i < $key; $i++) {
echo 'addOrder(affiliate_id = ' . $value . ') . '<br>';
}
}
the functions
function unique_array($array) {
return array_unique($array, SORT_NUMERIC);
}
function arrayDuplicate($array) {
return array_unique(array_diff_assoc($array,array_unique($array)));
}
function array_value_count($match, $array) {
$count = 0;
foreach ($array as $key => $value)
{
if ($value == $match)
{
$count++;
}
}
return $count;
}
to fix the duplicates breaking the code I have tried this
if(count($array_unique) - count($match_count_unique) == 1 ) // do something
or
if(count($array_unique) != count($match_count_unique) == 1 ) // do something
How would I know where to add the missing duplicate value count and array items correctly without them getting out of sync? OR Is there a better way of doing this?
Taken from How do I count occurrence of duplicate items in array
$array = array(12,43,66,21,56,43,43,78,78,100,43,43,43,21);
$vals = array_count_values($array);
echo 'No. of NON Duplicate Items: '.count($vals).'<br><br>';
print_r($vals);
Result
No. of NON Duplicate Items: 7
Array
(
[12] => 1
[43] => 6
[66] => 1
[21] => 2
[56] => 1
[78] => 2
[100] => 1
)
Duplicate items = (Array Size) - (Total Number of Unique Values)
<?php
$affiliates = array(11,11,12,12,13,13,13,14,14,14,14);
// get an array whose keys are the aff# and
//the values are how many times they occur
$dupes = array();
foreach ($affiliates as $aff) {
$dupes[$aff]++;
}
// remove the 1's since those aren't dupes
$dupes = preg_grep('~^1$~',$dupes,PREG_GREP_INVERT);
// de-dupe the original array
$affiliates = array_unique($affiliates);
// for each duped affiliate...
foreach ($dupes as $aff => $affCount) {
// for each time it was duped..
for ($c=0;$c<$affCount;$c++) {
// do something. $aff is the aff# like 11
}
}
?>
I have an array of my inventory (ITEMS A & B)
Items A & B are sold as sets of 1 x A & 2 x B.
The items also have various properties which don't affect how they are distributed into sets.
For example:
$inventory=array(
array("A","PINK"),
array("A","MAUVE"),
array("A","ORANGE"),
array("A","GREY"),
array("B","RED"),
array("B","BLUE"),
array("B","YELLOW"),
array("B","GREEN"),
array("B","BLACK")
);
I want to redistribute the array $inventory to create $set(s) such that
$set[0] => Array
(
[0] => array(A,PINK)
[1] => array(B,RED)
[2] => array(B,BLUE)
)
$set[1] => Array
(
[0] => array(A,MAUVE)
[1] => array(B,YELLOW)
[2] => array(B,GREEN)
)
$set[2] => Array
(
[0] => array(A,ORANGE)
[1] => array(B,BLACK)
[2] => NULL
)
$set[3] => Array
(
[0] => array(A,GREY)
[1] => NULL
[2] => NULL
)
As you can see. The items are redistributed in the order in which they appear in the inventory to create a set of 1 x A & 2 x B. The colour doesn't matter when creating the set. But I need to be able to find out what colour went into which set after the $set array is created. Sets are created until all inventory is exhausted. Where an inventory item doesn't exist to go into a set, a NULL value is inserted.
Thanks in advance!
I've assumed that all A's come before all B's:
$inventory=array(
array("A","PINK"),
array("A","MAUVE"),
array("A","ORANGE"),
array("A","GREY"),
array("B","RED"),
array("B","BLUE"),
array("B","YELLOW"),
array("B","GREEN"),
array("B","BLACK")
);
for($b_start_index = 0;$b_start_index<count($inventory);$b_start_index++) {
if($inventory[$b_start_index][0] == 'B') {
break;
}
}
$set = array();
for($i=0,$j=$b_start_index;$i!=$b_start_index;$i++,$j+=2) {
isset($inventory[$j])?$temp1=$inventory[$j]:$temp1 = null;
isset($inventory[$j+1])?$temp2=$inventory[$j+1]:$temp2 = null;
$set[] = array( $inventory[$i], $temp1, $temp2);
}
To make it easier to use your array, you should make it something like this
$inv['A'] = array(
'PINK',
'MAUVE',
'ORANGE',
'GREY'
);
$inv['B'] = array(
'RED',
'BLUE',
'YELLOW',
'GREEN',
'BLACK'
);
This way you can loop through them separately.
$createdSets = $setsRecord = $bTemp = array();
$bMarker = 1;
$aIndex = $bIndex = 0;
foreach($inv['A'] as $singles){
$bTemp[] = $singles;
$setsRecord[$singles][] = $aIndex;
for($i=$bIndex; $i < ($bMarker*2); ++$i) {
//echo $bIndex.' - '.($bMarker*2).'<br/>';
if(empty($inv['B'][$i])) {
$bTemp[] = 'null';
} else {
$bTemp[] = $inv['B'][$i];
$setsRecord[$inv['B'][$i]][] = $aIndex;
}
}
$createdSets[] = $bTemp;
$bTemp = array();
++$bMarker;
++$aIndex;
$bIndex = $bIndex + 2;
}
echo '<pre>';
print_r($createdSets);
print_r($setsRecord);
echo '</pre>';
To turn your array into an associative array, something like this can be done
<?php
$inventory=array(
array("A","PINK"),
array("A","MAUVE"),
array("A","ORANGE"),
array("A","GREY"),
array("B","RED"),
array("B","BLUE"),
array("B","YELLOW"),
array("B","GREEN"),
array("B","BLACK")
);
$inv = array();
foreach($inventory as $item){
$inv[$item[0]][] = $item[1];
}
echo '<pre>';
print_r($inv);
echo '</pre>';
Maybe you can use this function, assuming that:
... $inventory is already sorted (all A come before B)
... $inventory is a numeric array staring at index zero
// $set is the collection to which the generated sets are appended
// $inventory is your inventory, see the assumptions above
// $aCount - the number of A elements in a set
// $bCount - the number of B elements in a set
function makeSets(array &$sets, array $inventory, $aCount, $bCount) {
// extract $aItems from $inventory and shorten $inventory by $aCount
$aItems = array_splice($inventory, 0, $aCount);
$bItems = array();
// iterate over $inventory until a B item is found
foreach($inventory as $index => $item) {
if($item[0] == 'B') {
// extract $bItems from $inventory and shorten $inventory by $bCount
// break out of foreach loop after that
$bItems = array_splice($inventory, $index, $bCount);
break;
}
}
// append $aItems and $bItems to $sets, padd this array with null if
// less then $aCount + $bCount added
$sets[] = array_pad(array_merge($aItems, $bItems), $aCount + $bCount, null);
// if there are still values left in $inventory, call 'makeSets' again
if(count($inventory) > 0) makeSets($sets, $inventory, $aCount, $bCount);
}
$sets = array();
makeSets($sets, $inventory, 1, 2);
print_r($sets);
Since you mentioned that you dont have that much experience with arrays, here are the links to the php documentation for the functions I used in the above code:
array_splice — Remove a portion of the array and replace it with something else
array_merge — Merge one or more arrays
array_pad — Pad array to the specified length with a value
This code sorts inventory without any assumption on inventory ordering. You can specify pattern (in $aPattern), and order is obeyed. It also fills lacking entries with given default value.
<?php
# config
$aInventory=array(
array("A","PINK"),
array("A","MAUVE"),
array("A","ORANGE"),
array("A","GREY"),
array("B","RED"),
array("B","BLUE"),
array("B","YELLOW"),
array("B","GREEN"),
array("B","BLACK"),
array("C","cRED"),
array("C","cBLUE"),
array("C","cYELLOW"),
array("C","cGREEN"),
array("C","cBLACK")
);
$aPattern = array('A','B','A','C');
$mDefault = null;
# preparation
$aCounter = array_count_values($aPattern);
$aCurrentCounter = $aCurrentIndex = array_fill_keys(array_unique($aPattern),0);
$aPositions = array();
$aFill = array();
foreach ($aPattern as $nPosition=>$sElement){
$aPositions[$sElement] = array_keys($aPattern, $sElement);
$aFill[$sElement] = array_fill_keys($aPositions[$sElement], $mDefault);
} // foreach
$nTotalLine = count ($aPattern);
$aResult = array();
# main loop
foreach ($aInventory as $aItem){
$sElement = $aItem[0];
$nNeed = $aCounter[$sElement];
$nHas = $aCurrentCounter[$sElement];
if ($nHas == $nNeed){
$aCurrentIndex[$sElement]++;
$aCurrentCounter[$sElement] = 1;
} else {
$aCurrentCounter[$sElement]++;
} // if
$nCurrentIndex = $aCurrentIndex[$sElement];
if (!isset($aResult[$nCurrentIndex])){
$aResult[$nCurrentIndex] = array();
} // if
$nCurrentPosition = $aPositions[$sElement][$aCurrentCounter[$sElement]-1];
$aResult[$nCurrentIndex][$nCurrentPosition] = $aItem;
} // foreach
foreach ($aResult as &$aLine){
if (count($aLine)<$nTotalLine){
foreach ($aPositions as $sElement=>$aElementPositions){
$nCurrentElements = count(array_keys($aLine,$sElement));
if ($aCounter[$sElement] != $nCurrentElements){
$aLine = $aLine + $aFill[$sElement];
} // if
} // foreach
} // if
ksort($aLine);
# add empty items here
} // foreach
# output
var_dump($aResult);
Generic solution that requires you to specify a pattern of the form
$pattern = array('A','B','B');
The output will be in
$result = array();
The code :
// Convert to associative array
$inv = array();
foreach($inventory as $item)
$inv[$item[0]][] = $item[1];
// Position counters : int -> int
$count = array_fill(0, count($pattern),0);
$out = 0; // Number of counters that are "out" == "too far"
// Progression
while($out < count($count))
{
$elem = array();
// Select and increment corresponding counter
foreach($pattern as $i => $pat)
{
$elem[] = $inv[ $pat ][ $count[$i]++ ];
if($count[$i] == count($inv[$pat]))
$out++;
}
$result[] = $elem;
}