Sorting Elements of an Array by number in string? - php

I have a .txt file that looks like this:
john 1000
mike 8393
tom 1000
bob 233
roger 2
daniel 233
... ...
I need to put every line into array and sort by number size without losing what name goes with what number.
Also some numbers are repeating through the file.
Finally I want to echo elements of an array sorted by number size.

You could break each line into an array of integers and strings (split your current strings on the space) and then sort the array using ksort (assuming the key is the integer) and there you go!
You can of course alternatively use a more robust sort, but this will get you there.
You can then print it by using print_r to print the human readable version of the array
http://php.net/manual/en/array.sorting.php
http://php.net/manual/en/function.print-r.php

If you're running php 5.3+
$fileLines = explode("\n", $fileContents);
usort($fileLines, function($a, $b) {
$aNumber = (int)substr($a, strpos($a, ' ')+1);
$bNumber = (int)substr($b, strpos($b, ' ')+1);
if($aNumber === $bNumber) {
return 0;
}
return $aNumber > $bNumber ? 1 : -1;
});
If you're running a lower version, convert the function into a global function and provide the name as a callback string.
usort($fileLines, 'sortlines');
function sortlines($a, $b) {
$aNumber = (int)substr($a, strpos($a, ' ')+1);
$bNumber = (int)substr($b, strpos($b, ' ')+1);
if($aNumber === $bNumber) {
return 0;
}
return $aNumber > $bNumber ? 1 : -1;
}
then
var_dump($fileLines);

You can go three ways:
1.Create two-dimensional array with numeric indexes and sort it with usort(); and lambda function
$list = array(array('name' => 'john', 'number' => 1000),
array('name' => 'mike', 'number' => 8393),
array('name' => 'tom', 'number' => 1000)
);
$by = 'number';
usort($list, function($first, $second) use ($by)
{
if ($first[$by] > $second[$by] { return 1; }
elseif (first[$by] < $second[$by]) { return -1; }
return 0;
}
);
2.Create array with indexes as names and sort it with sort();
$list = array('john' => 1000,
'mike' => 8393,
'tom' => 1000
);
sort($list);
3.Create array with indexes as numbers and sort it with ksort();
$list = array(1000 => 'john',
8393 => 'mike',
1000 => 'tom'
);
ksort($list);
If you choose first way you can address the element as
$list[0][name] = 'bob'
$list[1][number] = 1000;
Second
$list['john'] = 1001;
Third
$list[1000] = 'bob';
In last two ways you should use foreach to go through array
Use
print_r($list);
or
var_dump($list);
to print the array, or create your own code
P.S. Don't forget thar calling usort with lambda function is PHP 5.3 way, if you use earlier version of PHP, you should use discrete function

Related

Cluster PHP array

Let's say I have an array of items with each item a value. I'd like to
create a new array where the items are clustered by their relative distance to each other.
When an item has a distance of one to another item, they belong to each other.
$input = [
'item-a' => 1,
'item-b' => 2,
'item-c' => 3,
'item-d' => 5,
];
$output = [
['item-a', 'item-b'],
['item-b', 'item-c'],
['item-d'],
];
This will create an output of overlapping arrays. What I want is that, because item-a and item-b are related, and item-b is also
related to item-c, I'd like to group item-a, item-b, and item-c to each other. The distance to item-c and item-d is greater than
1 so it will for a cluster of itself.
$output = [
['item-a', 'item-b', 'item-c'],
['item-d'],
];
How do I even start coding this?
Thanks in advance and have a nice day!
This can only be tested in your environment but here is what it does
it attempts to find relative distances based on array index 0's hash
it resorts the input array by distances (assuming that in this stage some will be positive and some negative) - that gives us the info to put the hash array in an order
Take this new array and put the hash back in
build a final output array measuring distances and sorting the level of output array by a threshhold.
I put in a couple dummy functions to return distances, obviously replace with your own. This might need tweaking but at this point, it's in your hands.
<?php
// example code
$input = [
'item-a' => 'a234234d',
'item-f' => 'h234234e',
'item-h' => 'e234234f',
'item-b' => 'f234234g',
'item-m' => 'd234234j',
'item-d' => 'm234234s',
'item-e' => 'n234234d',
'item-r' => 's234234g',
'item-g' => 'f234234f',
];
function getDistanceFrom($from, $to) {
return rand(-3,3);
}
function getDistanceFrom2($from, $to) {
return rand(0,7);
}
// first sort by relative distance from the first one
$tmp = [];
$ctr = 0;
foreach ($input as $item => $hash) {
if ($ctr === 0) { $ctr ++; continue; }
$tmp[$item]=getDistanceFrom(reset($input), $hash);
}
uasort($tmp, function ($a, $b)
{
return ($a < $b) ? -1 : 1;
});
//now they're in order, ditch the relative distance and put the hash back in
$sortedinput = [];
foreach ($tmp as $item => $d) {
$sortedinput[$item] = $input[$item];
}
$output=[];
$last=0;
$level=0;
$thresh = 3; // if item is within 3 of the previous, group
foreach($sortedinput as $v=>$i) {
$distance = getDistanceFrom2($last, $i);
if (abs($distance) > $thresh) $level++;
$output[$level][]=array("item" => $v, "distance" => $distance, "hash" => $i);
$last = $i;
}
print_r($output);

How to merge multiple arrays and interpolate the sums of values if keys don't match?

I'm looking for a function to merge 2 or more arrays into an output array to use on a graph. The X axes (array key) are dates & the Y are Dollars (array value). Sometimes the dates/keys may match up or they may not. Also the lengths or the array may not be the same. I've found examples on how to do this when the keys match: How to sum all column values in multi-dimensional array?
But I'd like to interpolate the values when they don't match.
I've put this graph together as an example of the input & output I'm try to get.
The 2 red input arrays would be:
$a1 = array(
"2019-01-01" => 0
,"2019-01-15" => 1000
,"2019-02-05" => 2000
,"2019-02-19" => 4000
);
$a2 = array(
"2019-01-22" => 0
,"2019-02-05" => 1000
,"2019-02-12" => 2000
,"2019-02-26" => 3000
);
And the blue output array would be:
Array
(
[2019-01-01] => 0
[2019-01-15] => 1000
[2019-01-22] => 1333
[2019-02-05] => 3000
[2019-02-12] => 5000
[2019-02-19] => 6500
[2019-02-26] => 7000
)
I've got the interpolate function working:
echo getFeeFromDates('2019-1-15', 1000, '2019-2-5', 2000, '2019-1-22');
function getFeeFromDates ($date1, $fee1, $date2, $fee2, $getDate) {
$start = strtotime($date1);
$end = strtotime($date2);
$find = strtotime($getDate);
$days_between = ceil(abs($end - $start) / 86400);
$fee = abs($fee2 - $fee1);
$feePerDay = $fee / $days_between;
$findDays = ceil(abs($find - $start) / 86400);
$myFee = number_format(($feePerDay * $findDays) + $fee1, 2);
return $myFee;
}
This will return 1333.33 as the interpolated value on the 2019-1-22 date.
I'm just having trouble wrapping my head around looping through the multiple arrays & when to interpolate the value vs. just adding the values. Any help would be appreciated.
You could create a helper function that takes one key/value array and an array of keys, which returns a key/value array that will contain all the keys in the second array, and interpolated values where needed. The array of keys should have all the keys that exist in the first array, but can have more (not less):
function interpolate($a, $keys) {
foreach($keys as $key) {
if (key($a) === $key) {
$prevValue = $result[$key] = current($a);
$prevKey = $key;
next($a);
} else if (empty($prevKey)) {
$result[$key] = 0;
} else {
$result[$key] = current($a) === false ? $prevValue
: $prevValue + (current($a) - $prevValue)
* (strtotime($key) - strtotime($prevKey))
/ (strtotime(key($a)) - strtotime($prevKey));
}
}
return $result;
}
Now with this function it is easy to get the desired result. Let's say you have $a1 and $a2 as in your example. Then do:
$keys = array_keys(array_merge($a1, $a2));
sort($keys);
$b1 = interpolate($a1, $keys);
$b2 = interpolate($a2, $keys);
foreach($b1 as $key => $value) {
$sum[$key] = $value + $b2[$key];
}
$sum will have the desired result.
More than 2 input arrays
When you have an array of such input arrays, let's call it $a, then you can use this code:
$keys = array_keys(array_merge(...$a));
sort($keys);
$b = array_map(function ($a1) use ($keys) {
return interpolate($a1, $keys);
}, $a);
foreach($keys as $key) {
$sum[$key] = array_sum(array_column($b, $key));
}

Which ways to clear array is better in this situation?

I intend to create a multidimensional array through a loop. My idea is that inside the loop function, I would assign elements into a single array, then the single array would assign into another big array before i empty the single array for the next loop process.
E.g.
for($i=0;$i<5;$i++){
$arr1['a'] = $i;
$arr1['b'] = $i+=2;
$arr2[] = $arr; //assign the arr1 into the arr2
$arr1= []; //clear arr1 again for the next looping
}
var_dump($arr2);
And the the result would be:
array (size=2)
0 =>
array (size=2)
'a' => int 0
'b' => int 2
1 =>
array (size=2)
'a' => int 3
'b' => int 5
However I find out that there are actually quite many ways to empty an array and Im not sure about that the way I empty the array is better/more efficient than the other ways such as unset or reinitialize the array like $arr1 = array()in this case. I look through online but there are not much in comparing these ways. Any suggestion to improve the performance on this or this is just as good as other ways? By looking at these:
unset($arr)
$arr = array()
$arr = []
And perhaps other ways?
Instead of making one extra array (smaller array) and then assigning the value to the bigger array, you can do something like this:
for($i=0;$i<5;$i++){
$arr2[$i]['a'] = $i;
$arr2[$i]['b'] = $i+2;
}
var_dump($arr2);
Output:
array (size=2)
0 =>
array (size=2)
'a' => int 0
'b' => int 2
1 =>
array (size=2)
'a' => int 3
'b' => int 5
In this case, you will not need to empty the (smaller) array again and again. Also it will save your memory space as you are using just one array.
If you want to execute a comparison, yo can do it with help of this code.
Ref: https://gist.github.com/blongden/2352583
It would look like so:
<?php
$calibration = benchmark(function() {
for($i=0;$i<5;$i++){
$arr2[$i]['a'] = $i;
$arr2[$i]['b'] = $i+2;
}
});
$benchmark = benchmark(function(){
for($i=0;$i<5;$i++){
$arr1['a'] = $i;
$arr1['b'] = $i+=2;
$arr2[] = $arr1;
$arr1 = [];
}
});
echo "Calibration run: ".number_format($calibration)."/sec\n";
echo "Benchmark run: ".number_format($benchmark)."/sec\n";
echo 'Approximate code execution time (seconds): '.number_format((1/$benchmark) - (1/$calibration), 10);
function benchmark($x)
{
$start = $t = microtime(true);
$total = $c = $loop = 0;
while (true) {
$x();
$c++;
$now = microtime(true);
if ($now - $t > 1) {
$loop++;
$total += $c;
list($t, $c) = array(microtime(true), 0);
}
if ($now - $start > 2) {
return round($total / $loop);
}
}
}
Output for a test run in my env (PHP 7.2.8-1+ubuntu16.04.1+deb.sury.org+1)
Calibration run: 258,534/sec
Benchmark run: 259,401/sec
Approximate code execution time (seconds): -0.0000000129
You will see that the given function by Nikhil is actually faster.
Output for the following function being run:
for($i=0;$i<5;$i++){
$arr2[$i]['a'] = $i;
$arr2[$i]['b'] = $i+2;
}
Calibration run: 209,614/sec
Benchmark run: 209,773/sec
Approximate code execution time (seconds): -0.0000000036
Feel free to play around your own tests

php arrays sorting contained values

Ok, first of all, I'm not even sure the title is right, if so, I'm sorry.
I have this loop here which is the result of a MongoDB query:
foreach($cursor as $obj) {
$monster = $obj["type"];
$strenght = $obj["strenght"];
$obj["value"] = rand(5, 15);
}
now, I have put rand there to signify that value changes for each iteration. Now i want that this array, when is printed, is ordered by that $obj["value"], and be able to chose if ascending or descending.
ok, I have tried this
foreach($cursor as $obj) {
$type = $obj["monster"];
$strenght = $obj["strenght"];
$obj["value"] = rand(5, 15);
$newarr[] = $obj;
}
usort($newarr, "cmp");
function cmp($a, $b)
{ return $b['value'] < $a['value']; }
foreach ($newarr as $obj)
{
echo $obj['value'] . $obj['type'] . "<br/>";
}
As I expected, the
$obj["value"] = rand(5, 15);
does not get lost at every iteration in fact, the $newarr contains that value, the problem is that it does not sort them at all. The items are printed in the same order as they were put inside the array. Any help?
Thanks
function mysort ($arr,$d='asc') {
global $dir;
$dir = $d;
uasort($arr, 'cmp');
return ($arr);
}
function cmp ($a, $b) {
global $dir;
if($a['value'] == $b['value']) return 0;
else {
if(strtolower($dir) == 'asc')
return ($a['value'] > $b['value']) ? 1 : -1;
else if(strtolower($dir) == 'disc')
return ($a['value'] > $b['value']) ? -1 : 1;
}
}
print_r(mysort($obj, 'disc'));
ACCORDING TO YOUR UPDATE
try this cmp()
function cmp($a, $b) {
if($a['value'] == $b['value']) return 0;
return $a['value'] > $b['value'] ? 1 : -1;
}
First of all, by doing $obj["value"] = rand(..) you are assigning same array variable with different values multiple times. By the end of the loop, the variable will only contain one value.
You probably were trying to do this
$obj["value"][] = rand(5, 15); //This adds a new random item to the array contained in $obj['value'] each time
When you have an array items, you can sort them by using sort()[ascending] function rsort()[Descending[
$sorted = sort($obj["value"]);
print_r($sorted);
You loose this value
$obj["value"] = rand(5, 15);
at each iteration i guess. Check this link for php foreach loop:
PHP FOREACH
For sorting u can use sort function of php:
PHP SORT
Your foreach will not really generate anything useful. So I created an example array to illustrate the principle:
$array = array(
array(
'type' => 'type a',
'strength' => '10',
'value' => '12',
),
array(
'type' => 'type b',
'strength' => '12',
'value' => '15',
),
array(
'type' => 'type c',
'strength' => '11',
'value' => '6',
),
);
Now you want this multi dimensional array to be sorted by the value, so you would have a list if it was descending order, of type b, type a and then type c.
The function you are looking for is array_multisort(). Here a sample how to use it on this particular instance. First you need to create that sorter array for the function to do it's job. Then just use multisort and you are done.
$sorter = array();
foreach ($array as $key => $row) {
$sorter[$key] = $row['value'];
}
array_multisort($sorter, SORT_DESC, $array);
Now $array has been resorted according to the specifications. Use SORT_ASC to reverse the sorting.
Added code: Just for sake of completeness, here is your foreach code that should technically create what you wanted to in the first place:
$array = array();
foreach($cursor as $obj) {
$row = array();
$row['type'] = $obj['type'];
$row['strength'] = $obj['strenght'];
$row['value'] = rand(5, 15);
$array[] = $row;
}
Also you have a typo in strength :)
Since you are working with integer values, you can simply use this:
usort($newarr, "cmp");
function cmp($a, $b) {
return $b['value'] - $a['value'];
}
It will sort your keys in descending order, if you want to change that to ascending, swap $a and $b.
Sorting functions generally expect the compare function to return a value of 0 if the items are the same, <0 if the first argument is less than the second (whatever less means in the particular case) and >0 if the first argument is greater than the second.
With integer values this can simply be written as $a - $b or $b - $a.

Replace non-specified array values with 0

I want to replace all array values with 0 except work and home.
Input:
$array = ['work', 'homework', 'home', 'sky', 'door']
My coding attempt:
$a = str_replace("work", "0", $array);
Expected output:
['work', 0, 'home', 0, 0]
Also my input data is coming from a user submission and the amount of array elements may be very large.
A bit more elegant and shorter solution.
$aArray = array('work','home','sky','door');
foreach($aArray as &$sValue)
{
if ( $sValue!='work' && $sValue!='home' ) $sValue=0;
}
The & operator is a pointer to the particular original string in the array. (instead of a copy of that string)
You can that way assign a new value to the string in the array. The only thing you may not do is anything that may disturb the order in the array, like unset() or key manipulation.
The resulting array of the example above will be
$aArray = array('work','home', 0, 0)
A loop will perform a series of actions many times. So, for each element in your array, you would check if it is equal to the one you want to change and if it is, change it. Also be sure to put quote marks around your strings
//Setup the array of string
$asting = array('work','home','sky','door')
/**
Loop over the array of strings with a counter $i,
Continue doing this until it hits the last element in the array
which will be at count($asting)
*/
for($i = 0; $i < count($asting);$i++){
//Check if the value at the 'ith' element in the array is the one you want to change
//if it is, set the ith element to 0
if ($asting[$i] == 'work' || $asting[$i] == 'home')
$asting[$i] = 0;
}
Here is some suggested reading:
http://www.php.net/manual/en/language.types.array.php
http://www.php.net/manual/en/language.control-structures.php
But if you are struggling on stuff such as looping, you may want to read some introductory programming material. Which should help you really understand what's going on.
A bit other and much quicker way, but true, need a loop:
//Setup the array of string
$asting = array('bar', 'market', 'work', 'home', 'sky', 'door');
//Setup the array of replacings
$replace = array('home', 'work');
//Loop them through str_replace() replacing with 0 or any other value...
foreach ($replace as $val) $asting = str_replace($val, 0, $asting);
//See what results brings:
print_r ($asting);
Will output:
Array
(
[0] => bar
[1] => market
[2] => 0
[3] => 0
[4] => sky
[5] => door
)
An alternative using array_map:
$original = array('work','home','sky','door');
$mapped = array_map(function($i){
$exclude = array('work','home');
return in_array($i, $exclude) ? 0 : $i;
}, $original);
you may try array_walk function:
function zeros(&$value)
{
if ($value != 'home' && $value != 'work'){$value = 0;}
}
$asting = array('work','home','sky','door','march');
array_walk($asting, 'zeros');
print_r($asting);
You can also give array as a parameter 1 and 2 on str_replace...
Just a small point to the for loop. Many dont realize the second comparing task is done every new iteration. So if it was a case of big array or calculation you could optimize loop a bit by doing:
for ($i = 0, $c = count($asting); $i < $c; $i++) {...}
You may also want to see http://php.net/manual/en/function.array-replace.php for original problem unless the code really is final :)
Try This
$your_array = array('work','home','sky','door');
$rep = array('home', 'work');
foreach($rep as $key=>$val){
$key = array_search($val, $your_array);
$your_array[$key] = 0;
}
print_r($your_array);
There are a few techniques on this page that make zero iterated function calls -- which is good performance-wise. For best maintainability, I recommend separating your list of targeted string as a lookup array. By modifying the original array values by reference, you can swiftly replace whole strings and null coalesce non-targeted values to 0.
Code: (Demo)
$array = ['work', 'homework', 'home', 'sky', 'door'];
$keep = ['work', 'home'];
$lookup = array_combine($keep, $keep);
foreach ($array as &$v) {
$v = $lookup[$v] ?? 0;
}
var_export($array);
Output:
array (
0 => 'work',
1 => 0,
2 => 'home',
3 => 0,
4 => 0,
)
You can very easily, cleanly extend your list of targeted strings by merely extending $keep.
If you don't want a classic loop, you can use the same technique without modifying the original array. (Demo)
var_export(
array_map(fn($v) => $lookup[$v] ?? 0, $array)
);
this my final code
//Setup the array of string
$asting = array('work','home','sky','door','march');
/**
Loop over the array of strings with a counter $i,
Continue doing this until it hits the last element in the array
which will be at count($asting)
*/
for($i = 0; $i < count($asting); $i++) {
//Check if the value at the 'ith' element in the array is the one you want to change
//if it is, set the ith element to 0
if ($asting[$i] == 'work') {
$asting[$i] = 20;
} elseif($asting[$i] == 'home'){
$asting[$i] = 30;
}else{
$asting[$i] = 0;
}
echo $asting[$i]."<br><br>";
$total += $asting[$i];
}
echo $total;

Categories