Related
I have an array like this,
$array = array(
1,2,3,'4>12','13.1','13.2','14>30'
);
I want to find any value with an ">" and replace it with a range().
The result I want is,
array(
1,2,3,4,5,6,7,8,9,10,11,12, '13.1', '13.2', 14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30
);
My understanding:
if any element of $array has '>' in it,
$separate = explode(">", $that_element);
$range_array = range($separate[0], $separate[1]); //makes an array of 4 to 12.
Now somehow replace '4>12' of with $range_array and get a result like above example.
May be I can find which element has '>' in it using foreach() and rebuild $array again using array_push() and multi level foreach. Looking for a more elegant solution.
You can even do it in a one-liner like this:
$array = array(1,2,3,'4>12','13.1','13.2','14>30');
print_r(array_reduce(
$array,
function($a,$c){return array_merge($a,#range(...array_slice(explode(">","$c>$c"),0,2)));},
[]
));
I avoid any if clause by using range() on the array_slice() array I get from exploding "$c>$c" (this will always at least give me a two-element array).
You can find a little demo here: https://rextester.com/DXPTD44420
Edit:
OK, if the array can also contain non-numeric values the strategy needs to be modified: Now I will check for the existence of the separator sign > and will then either merge some cells created by a range() call or simply put the non-numeric element into an array and merge that with the original array:
$array = array(1,2,3,'4>12','13.1','64+2','14>30');
print_r(array_reduce(
$array,
function($a,$c){return array_merge($a,strpos($c,'>')>0?range(...explode(">",$c)):[$c]);},
[]
));
See the updated demo here: https://rextester.com/BWBYF59990
It's easy to create an empty array and fill it while loop a source
$array = array(
1,2,3,'4>12','13.1','13.2','14>30'
);
$res = [];
foreach($array as $x) {
$separate = explode(">", $x);
if(count($separate) !== 2) {
// No char '<' in the string or more than 1
$res[] = $x;
}
else {
$res = array_merge($res, range($separate[0], $separate[1]));
}
}
print_r($res);
range function will help you with this:
$array = array(
1,2,3,'4>12','13.1','13.2','14>30'
);
$newArray = [];
foreach ($array as $item) {
if (strpos($item, '>') !== false) {
$newArray = array_merge($newArray, range(...explode('>', $item)));
} else {
$newArray[] = $item;
}
}
print_r($newArray);
I am trying to sort it in a repeating, sequential pattern of numerical order with the largest sets first.
Sample array:
$array = [1,1,1,2,3,2,3,4,5,4,4,4,5,1,2,2,3];
In the above array, I have the highest value of 5 which appears twice so the first two sets would 1,2,3,4,5 then it would revert to the second, highest value set etc.
Desired result:
[1,2,3,4,5,1,2,3,4,5,1,2,3,4,1,2,4]
I am pretty sure I can split the array into chunks of the integer values then cherrypick an item from each subarray sequentially until there are no remaining items, but I just feel that this is going to be poor for performance and I don't want to miss a simple trick that PHP can already handle.
Here's my attempt at a very manual loop using process, the idea is to simply sort the numbers into containers for array_unshifting. I'm sure this is terrible and I'd love someone to do this in five lines or less :)
$array = array(1,1,1,2,3,2,3,4,5,4,4,4,5,1,2,2,3);
sort($array);
// Build the container array
$numbers = array_fill_keys(array_unique($array),array());
// Assignment
foreach( $array as $number )
{
$numbers[ $number ][] = $number;
}
// Worker Loop
$output = array();
while( empty( $numbers ) === false )
{
foreach( $numbers as $outer => $inner )
{
$output[] = array_shift( $numbers[ $outer ] );
if( empty( $numbers[ $outer ] ) )
{
unset( $numbers[ $outer ] );
}
}
}
var_dump( $output );
I think I'd look at this not as a sorting problem, but alternating values from multiple lists, so rather than coming up with sets of distinct numbers I'd make sets of the same number.
Since there's no difference between one 1 and another, all you actually need is to count the number of times each appears. It turns out PHP can do this for you with aaray_count_values.
$sets = array_count_values ($input);
Then we can make sure the sets are in order by sorting by key:
ksort($sets);
Now, we iterate round our sets, counting down how many times we've output each number. Once we've "drained" a set, we remove it from the list, and once we have no sets left, we're all done:
$output = [];
while ( count($sets) > 0 ) {
foreach ( $sets as $number => $count ) {
$output[] = $number;
if ( --$sets[$number] == 0 ) {
unset($sets[$number]);
}
}
}
This algorithm could be adapted for cases where the values are actually distinct but can be put into sets, by having the value of each set be a list rather than a count. Instead of -- you'd use array_shift, and then check if the length of the set was zero.
You can use only linear logic to sort using php functions. Here is optimized way to fill data structures. It can be used for streams, generators or anything else you can iterate and compare.
$array = array(1,1,1,2,3,2,3,4,5,4,4,4,5,1,2,2,3);
sort($array);
$chunks = [];
$index = [];
foreach($array as $i){
if(!isset($index[$i])){
$index[$i]=0;
}
if(!isset($chunks[$index[$i]])){
$chunks[$index[$i]]=[$i];
} else {
$chunks[$index[$i]][] = $i;
}
$index[$i]++;
}
$result = call_user_func_array('array_merge', $chunks);
print_r($result);
<?php
$array = array(1,1,1,2,3,2,3,4,5,4,4,4,5,1,2,2,3);
sort($array);
while($array) {
$n = 0;
foreach($array as $k => $v) {
if($v>$n) {
$result[] = $n = $v;
unset($array[$k]);
}
}
}
echo implode(',', $result);
Output:
1,2,3,4,5,1,2,3,4,5,1,2,3,4,1,2,4
New, more elegant, more performant, more concise answer:
Create a sorting array where each number gets its own independent counter to increment. Then use array_multisort() to sort by this grouping array, then sort by values ascending.
Code: (Demo)
$encounters = [];
foreach ($array as $v) {
$encounters[] = $e[$v] = ($e[$v] ?? 0) + 1;
}
array_multisort($encounters, $array);
var_export($array);
Or with a functional style with no global variable declarations: (Demo)
array_multisort(
array_map(
function($v) {
static $e;
return $e[$v] = ($e[$v] ?? 0) + 1;
},
$array
),
$array
);
var_export($array);
Old answer:
My advice is functionally identical to #El''s snippet, but is implemented in a more concise/modern/attractive fashion.
After ensuring that the input array is sorted, make only one pass over the array and push each re-encountered value into its next row of values. The $counter variable indicates which row (in $grouped) the current value should be pushed into. When finished looping and grouping, $grouped will have unique values in each row. The final step is to merge/flatten the rows (preserving their order).
Code: (Demo)
$grouped = [];
$counter = [];
sort($array);
foreach ($array as $v) {
$counter[$v] = ($counter[$v] ?? -1) + 1;
$grouped[$counter[$v]][] = $v;
}
var_export(array_merge(...$grouped));
I am trying to group some foreach results based on the first 3 sets of characters.
For example i am currently listing sku codes for products and they look like this:
REF-MUSBOM-0500-ORA
REF-PROCOF-0001-LAT
REF-WHEREF-0001-TRO
REF-WHEREF-0001-ORA
REF-SHAKER-0700-C/B
REF-CREMON-0100-N/A
REF-GLUSUL-0090-N/A
REF-CRECAP-0090-N/A
REF-ALBFER-0120-N/A
REF-TSHCOT-LARG-BLK
REF-TSHCOT-MEDI-BLK
REF-ALBMAG-0090-N/A
REF-GYMJUG-2200-N/A
REF-OMEGA3-0090-N/A
REF-NEXGEN-0060-N/A
REF-VITAD3-0100-N/A
REF-SSSHAK-0739-N/A
REF-GINKGO-0090-N/A
REF-DIGEZY-0090-N/A
REF-VEST00-MEDI-N/A
REF-VEST00-LARG-N/A
REF-CREMON-0250-N/A
REF-MSM----0250-N/A
REF-GRNTEA-0100-N/A
REF-COLOST-0100-N/A
REF-GLUCHO-0090-N/A
REF-ZINCMA-0100-N/A
REF-BETALA-0250-N/A
REF-DRIBOS-0250-N/A
REF-HMB000-0090-N/A
REF-ALACID-0090-N/A
REF-CLA000-0090-N/A
REF-ACETYL-0090-N/A
REF-NXGPRO-0090-N/A
REF-LGLUTA-0250-N/A
REF-BCAA20-0200-N/A
REF-FLAPJA-0012-ACR
REF-FLAPJA-0012-MAP
REF-LCARNI-0100-N/A
REF-CORDYC-0090-N/A
REF-CREMON-0500-N/A
REF-BCAAEN-0330-APP
REF-PREWKT-0300-FPU
REF-TESFUS-0090-N/A
REF-AMIIFUS-0300-GAP
REF-AMIIFUS-0300-WME
REF-BCAINT-0400-FPU
REF-KRILLO-0090-N/A
REF-AMIIFUS-0300-PLE
REF-AMIIFUS-0300-FPU
REF-BCAINT-0400-WME
REF-ENZQ10-0090-N/A
REF-THERMO-0100-N/A
REF-LGLUTA-0500-N/A
REF-RBAR00-0012-DCB
REF-RBAR00-0012-PBC
REF-RBAR00-0012-WCR
REF-IMHEAV-2200-CHO
REF-PROCOF-0012-N/A
REF-DIEPRO-0900-STR
REF-DIEPRO-0900-BOF
REF-DIEPRO-0900-CHO
REF-INWPRO-0900-VAN
REF-INWPRO-0900-BOF
REF-INWPRO-0900-BCS
REF-INWPRO-0900-CHO
REF-INWPRO-0900-CMI
REF-INWPRO-0900-RAS
REF-INWPRO-0900-STR
REF-INWPRO-0900-CIN
REF-INWPRO-0900-CPB
REF-EGGPRO-0900-CHO
REF-EGGPRO-0900-VAN
REF-MICCAS-0909-CHO
REF-MICCAS-0909-CMI
REF-MICCAS-0909-VAN
REF-MICCAS-0909-STR
REF-BCAA50-0500-N/A
REF-MICWHE-0909-STR
REF-MICWHE-0909-VAN
REF-MICWHE-0909-CHIO
REF-MICWHE-0909-BAN
REF-1STOXT-2030-STR
REF-1STOXT-2030-VAN
REF-1STOXT-2030-CHO
REF-MUSBOM-0600-BCH
REF-MUSBOM-0600-FPU
REF-MUSBCF-0600-BCH
REF-MUSBCF-0600-FPU
REF-VEGANP-2100-STR
REF-VEGANP-2100-CHO
REF-INMPRO-2270-CPB
REF-DIETMR-2400-CPB
REF-INMPRO-2270-SCR
REF-INMPRO-2270-VIC
REF-MATRIX-1800-FRU
REF-INMPRO-2270-BOF
REF-MATRIX-1800-CHO
REF-INMPRO-2270-CHO
REF-ONESTO-2100-CHO
In the above list there are 2 skus which are:
REF-WHEREF-0001-TRO
REF-WHEREF-0001-ORA
The first 3 sets of characters split by - are the same. What would be the best approach of grouping all results leaving me an array something like this:
Array
(
[REF-WHEREF-0001] => Array
(
[0] => REF-WHEREF-0001-TRO
[1] => REF-WHEREF-0001-ORA
)
)
Are the first 3 groups (excluding the multiple -) always 13 characters? Then do something like this:
<?php
$arr = ["REF-MUSBOM-0500-ORA",
"REF-PROCOF-0001-LAT",
"REF-WHEREF-0001-TRO",
"REF-WHEREF-0001-PPL"];
$resultArr = [];
foreach ($arr as $sku) {
$resultArr[substr($sku, 0, 15)][] = $sku;
}
var_dump($resultArr);
If that length varies you might want to work with a regex or the strpos() of the third -.
I must say that I think you could come up with this yourself, since you were already thinking in the right direction i.e. foreach()
EDIT: Because I found other solutions more elegant looking, I decided to compare efficiency. This solution is a lot faster than the other ones.
I always create a new array with the index that I need for group, try this:
$arr=array('REF-MUSBOM-0500-ORA',
'REF-PROCOF-0001-LAT',
'REF-WHEREF-0001-TRO');
$newarr=array();
foreach($arr as $a){
$b=explode('-',$a);
array_pop($b);
$b=implode("-", $b);
$newarr[$b][]=$a;
}
echo '<pre>',print_r($newarr),'</pre>';
You will need to pick a group with some basics use of explode, implode and str_replace.
What does this solution do.
loop through the array of your items
explode to get last item index of exploded string assuming that it
would be dynamic in the end
implode & str_replace again to find out string of group name
And last strpos & in_array to have sample reponse
Solution
$array = array(
'REF-MUSBOM-0500-ORA',
'REF-PROCOF-0001-LAT',
'REF-WHEREF-0001-TRO',
'REF-WHEREF-0001-ORA',
'REF-SHAKER-0700-C/B',
'REF-CREMON-0100-N/A',
'REF-GLUSUL-0090-N/A',
'REF-CRECAP-0090-N/A',
'REF-ALBFER-0120-N/A',
);
$new_array = array();
foreach ($array as $key => $val) {
$group_arr = explode('-', $val);
$end = end($group_arr);
$combined_group = implode('-', $group_arr);
$group = str_replace('-' . $end, '', $combined_group);
if (strpos($val, $group) !== false && !in_array($group, $new_array)) {
$new_array[$group][] = $val;
}
}
echo '<pre>';print_r($new_array);echo '</pre>';
See demo on Sandbox
I am a newbie in this and I have read lots of stuff about this matter (including some topics here), before starting this topic, but I do not quite get it yet, so I will ask for some help (if it is possible) :)
So, in the column that I want to print I have values like this on every row:
value1|value2|value5|value12|value25
value3|value5|value12|value14|value26|value32|value55
value1|value2|value14|value26|value31
The number of rows can be 3 or 1500+... So I want to merge the arrays and print those values sorted and without duplicates: value1, value2, value3, value5, value12, etc...
I have tried to explode the arrays, but I could not find out how to assign a variable to every array and merge them and all I have done is to print all values:
foreach ($rows as $areas) {
foreach (explode('|', $areas->value) as $area) {
var_dump($area);
}
}
Afterwards I have read somewhere this will be very slow if I have many rows (and I am going to have thousands), so I am stuck here and I do not know what else I could do...
I will appreciate any help and direction that you can give me, because it is too hard for me and I can not do it without help
Thank you in advance
You can store each value of your exploded string as key (if it's not an object nor array), it store only unique values. Then you have to just use array_keys() to get keys and sort returned array:
$rows = array(
'value1|value2|value5|value12|value25',
'value3|value5|value12|value14|value26|value32|value55',
'value1|value2|value14|value26|value31'
);
$results = array();
foreach ($rows as $row) {
$items = explode('|', $row);
foreach ($items as $item) {
$results[$item] = 0;
}
}
$results = array_keys($results);
sort($results, SORT_NATURAL);
Live demo on eval.in
There are two ways of doing this:
<?php
$str = 'value1|value2|value5|value12|value25';
$str1 = 'value3|value5|value12|value14|value26|value32|value55';
$str2 = 'value1|value2|value14|value26|value31';
//-- Method 1: Concat and make a single string and then explode and make a single array
$finalString = $str . '|' . $str1 . '|' . $str2;
print_r(array_unique(explode('|', $finalString)));
//-- Method 2: explode first and then merge into a single array
$strArr = explode('|', $str);
$strArr1 = explode('|', $str1);
$strArr2 = explode('|', $str2);
print_r(array_unique(array_merge($strArr, $strArr1, $strArr2)));
I have a page with a form where I post all my checkboxes into one array in my database.
The values in my database looks like this: "0,12,0,15,58,0,16".
Now I'm listing these numbers and everything works fine, but I don't want the zero values to be listed on my page, how am I able to search through the array and NOT list the zero values ?
I'm exploding the array and using a for each loop to display the values at the moment.
The proper thing to do is to insert a WHERE statement into your database query:
SELECT * FROM table WHERE value != 0
However, if you are limited to PHP just use the below code :)
foreach($values AS $key => $value) {
//Skip the value if it is 0
if($value == 0) {
continue;
}
//do something with the other values
}
In order to clean an array of elements, you can use the array_filter method.
In order to clean up of zeros, you should do the following:
function is_non_zero($value)
{
return $value != 0;
}
$filtered_data = array_filter($data, 'is_non_zero');
This way if you need to iterate multiple times the array, the zeros will already be deleted from them.
you can use array_filter for this. You can also specify a callback function in this function if you want to remove items on custom criteria.
Maybe try:
$out = array_filter(explode(',', $string), function ($v) { return ($v != 0); });
There are a LOT of ways to do this, as is obvious from the answers above.
While this is not the best method, the logic of this might be easier for phpnewbies to understand than some of the above methods. This method could also be used if you need to keep your original values for use in a later process.
$nums = '0,12,0,15,58,0,16';
$list = explode(',',$nums);
$newList = array();
foreach ($list as $key => $value) {
//
// if value does not equal zero
//
if ( $value != '0' ) {
//
// add to newList array
//
$newList[] = $value;
}
}
echo '<pre>';
print_r( $newList );
echo '</pre>';
However, my vote for the best answer goes to #Lumbendil above.
$String = '0,12,0,15,58,0,16';
$String = str_replace('0', '',$String); // Remove 0 values
$Array = explode(',', $String);
foreach ($Array AS $Values) {
echo $Values."<br>";
}
Explained:
You have your checkbox, lets say the values have been converted into a string. using str_replace we have removed all 0 values from your string. We have then created an array by using explode, and using the foreach loop. We are echoing out all the values of th array minux the 0 values.
Oneliner:
$string = '0,12,0,15,58,0,16';
echo preg_replace(array('/^0,|,0,|,0$/', '/^,|,$/'), array(',', ''), $string); // output 12,15,58,16