Sort array with duplicates by unique chunks - php

I want elements of that:
['1','1','2','2','2','3','3','3','3','3','3','3','4']
to be grouped into chunks with unique values, to make it look like that:
['1','2','3','4','1','2','3','2','3','3','3','3','3']
First "chunk" '1','2','3','4' contain all unique values without duplicates, second '1','2','3' etc.
The biggest problem is that my array is not combined from simple numbers, it is two-dimensional associative array, something like that:
[['id'=>'xd1c',...],['id'=>'ab2c',...],['id'=>'xd1c',...],['id'=>'xd1c',...],['id'=>'ab2c',...],['id'=>'xd1c',...],['id'=>'687d',...],...]
I don't have a lot of experience with algorythmics and advanced sorting and I feel a bit overwhelmed. Please if you could point me out in right direction with that.

Try this code :
$current = array('1','1','2','2','2','3','3','3','3','3','3','3','4');
$new = array();
while(!empty($current)){
foreach(array_keys(array_unique($current)) as $index){
$new[] = $current[$index];
unset($current[$index]);
}
}
print_r($new);

Related

Find index of value in associative array in php?

If you have any array $p that you populated in a loop like so:
$p[] = array( "id"=>$id, "Name"=>$name);
What's the fastest way to search for John in the Name key, and if found, return the $p index? Is there a way other than looping through $p?
I have up to 5000 names to find in $p, and $p can also potentially contain 5000 rows. Currently I loop through $p looking for each name, and if found, parse it (and add it to another array), splice the row out of $p, and break 1, ready to start searching for the next of the 5000 names.
I was wondering if there if a faster way to get the index rather than looping through $p eg an isset type way?
Thanks for taking a look guys.
Okay so as I see this problem, you have unique ids, but the names may not be unique.
You could initialize the array as:
array($id=>$name);
And your searches can be like:
array_search($name,$arr);
This will work very well as native method of finding a needle in a haystack will have a better implementation than your own implementation.
e.g.
$id = 2;
$name= 'Sunny';
$arr = array($id=>$name);
echo array_search($name,$arr);
Echoes 2
The major advantage in this method would be code readability.
If you know that you are going to need to perform many of these types of search within the same request then you can create an index array from them. This will loop through the array once per index you need to create.
$piName = array();
foreach ($p as $k=>$v)
{
$piName[$v['Name']] = $k;
}
If you only need to perform one or two searches per page then consider moving the array into an external database, and creating the index there.
$index = 0;
$search_for = 'John';
$result = array_reduce($p, function($r, $v) use (&$index, $search_for) {
if($v['Name'] == $search_for) {
$r[] = $index;
}
++$index;
return $r;
});
$result will contain all the indices of elements in $p where the element with key Name had the value John. (This of course only works for an array that is indexed numerically beginning with 0 and has no “holes” in the index.)
Edit: Possibly even easier to just use array_filter, but that will not return the indices only, but all array element where Name equals John – but indices will be preserved:
$result2 = array_filter($p, function($elem) {
return $elem["Name"] == "John" ? true : false;
});
var_dump($result2);
What suits your needs better, resp. which one is maybe faster, is for you to figure out.

Autofill array with empty data to match another array size

I have 2 sets of arrays:
$dates1 = array('9/12','9/13','9/14','9/15','9/16','9/17');
$data1 = array('5','3','7','7','22','18');
// for this dataset, the value on 9/12 is 5
$dates2 = array('9/14','9/15');
$data2 = array('12','1');
As you can see the 2nd dataset has fewer dates, so I need to "autofill" the reset of the array to match the largest dataset.
$dates2 = array('9/12','9/13','9/14','9/15','9/16','9/17');
$data2 = array('','','12','1','','');
There will be more than 2 datasets, so I would have to find the largest dataset, and run a function for each smaller dataset to properly format it.
The function I'd create is the problem for me. Not even sure where to start at this point. Also, I can format the date and data arrays differently (multidimensional arrays?) if for some reason that is better.
You can do this in a pretty straightforward manner using some array functions. Try something like this:
//make an empty array matching your maximum-sized data set
$empty = array_fill_keys($dates1,'');
//for each array you wish to pad, do this:
//make key/value array
$new = array_combine($dates2,$data2);
//merge, overwriting empty keys with data values
$new = array_merge($empty,$new);
//if you want just the data values again
$data2 = array_values($new);
print_r($data2);
It would be pretty easy to turn that into a function or put it into a for loop to operate on your array sets. Turning them into associative arrays of key/value pairs would make them easier to work with too I would think.
If datas are related will be painful to scatter them on several array.
The best solution would be model an object with obvious property names
and use it with related accessor.
From your question I haven't a lot of hint of what data are and then I have to guess a bit:
I pretend you need to keep a daily log on access on a website with downloads. Instead of using dates/data1/data2 array I would model a data structure similar to this:
$log = array(
array('date'=>'2011-09-12','accessCount'=>7,'downloadCount'=>3),
array('date'=>'2011-09-13','accessCount'=>9), /* better downloadsCount=>0 though */
array('date'=>'2011-09-15','accessCount'=>7,'downloadCount'=>3)
...
)
Using this data structure I would model a dayCollection class with methods add,remove,get,set, search with all methods returning a day instance (yes, the remove too) and according signature. The day Class would have the standard getter/setter for every property (you can resolve to magic methods).
Depending on the amount of data you have to manipulate you can opt to maintain into the collection just the object data (serialize on store/unserialize on retrieve) or the whole object.
It is difficult to show you some code as the question is lacking of details on your data model.
If you still want to pad your array than this code would be a good start:
$temp = array($dates, $data1, $data2);
$max = max(array_map('count',$temp));
$result = array_map( function($x) use($max) {
return array_pad($x,$max,0);
}, $temp);
in $result you have your padded arrays. if you want to substitute your arrays do a simple
list($dates, $data1, $data2) = array_map(....
You should use hashmaps instead of arrays to associate each date to a data.
Then, find the largest one, cycle through its keys with a foreach, and test the existence of the same key in the small one.
If it doesn't exist, create it with an empty value.
EDIT with code (for completeness, other answers seem definitely better):
$dates_data1 = array('9/12'=>'5', '9/13'=>'3', '9/14'=>'7' /* continued */);
$dates_data2 = array('9/14'=>'12', '9/15'=>'1');
#cycle through each key (date) of the longest array
foreach($dates_data1 as $key => $value){
#check if the key exists in the smallest and add '' value if it does not
if(!isset( $date_data2[$key] )){ $date_data2[$key]=''; }
}

how to compare two arrays and find the count of match elements

i have two arrays i.e$ar1=array("Mobile","shop","software","hardware");and$arr2=arry("shop","Mobile","shop","software","shop")
i want to compare the elements of arr2 to arr1 i.e
foreach($arr2 as $val)
{
if(in_array($val, $arr1))
{
//mycode to insert data into mysql table
variable++; // here there should be a variable that must be increamented when ever match found in $arr2 so that it can be saved into another table.
}
$query="update table set shop='$variable',Mobile='$variable'.......";
}
the $variable should be an integer value so that for later use i can use this variable(shop i.e in this example its value should be 3) to find the match.
My question is how can i get the variable that will increamented each time match found.
Sorry, I don't fully understand the purpose of your code. You can use array_intersect to get common values and array_diff to get the unique values when comparing two arrays.
i want to compare the elements of arr2 to arr1 i.e
Then you are essentially doing the same search for shop three times. It's inefficient. Why not sort and eliminate duplicates first?
Other issues. You are comparing arr2 values with the ones in arr1, which means the number of repetation for "shop" will not be 3 it will be one. Doing the opposite might give you the number of repetation of arr1[1] in arr2 =3.
There are multitude of ways to solve this problem. If efficiency is required,you might wish to sort so you don't have to go beyond a certain point (say s). You can learn to use indexes. Infact the whole datastructure is revolved around these kinds of things - from quick and dirty to efficient.
Not sure I understand the connection between your two arrays. But you can use this to count how many items are in your second array:
$items = array("shop","Mobile","shop","software","shop");
$count = array();
foreach($items as $item)
{
if(isset($count[$item]))
{
$count[$item]++;
}
else
{
$count[$item] = 1;
}
}
print_r($count); // Array ( [shop] => 3 [Mobile] => 1 [software] => 1 )

PHP: Recursively set array values to equal keys (Codeigniter)

I am taking over a large project, and a lot of nested arrays are defined for option select lists to be used with form_dropdown() and form_multiselect() in Codeigniter. However, these arrays simply have values set and not corresponding keys.
Here's an example:
$lists['roomItems'] = array('Private telephone','Television cable/satellite','Personal furniture/decorations','Computer','Radio');
$lists['busRoute'] = array('Yes','No');
$lists['transport'] = array('Medical appointments','Dental appointments','Dialysis center','Wound care center','Religious services',
'Shopping services');
What I'd like to do is recursively go through $lists and make the keys equivalent to the values. For a single array, I tried foreach($lists['roomItems'] as $key=>value) and tried setting the key equal to the value, but it didn't take.
Can anyone help? I have about 30 items in the $lists array plus other ones that I'd re-use this code, so simply manually changing the pointers isn't really something I'd like to do. Thanks!
mhmmm what about:
$newList = array();
foreach($lists as $k=>$v) $newList[$k] = array_combine($v,$v);
should do the trick

Sorting multi-dimensional array using array_multisort, where you don't know the dimensions of the array

I have a multi-dimensional array, which basically consists of one sub-array for each year. So, for example, if I had three year's worth of data, it might look like this:
$data[0] = Array(0,1,2,3,4,5,6,7);
$data[1] = Array(6,5,4,3,6,7,8,9);
$data[2] = Array(1,4,2,5,7,3,1,4);
Now I want to be able to sort those arrays on the basis of one of the years. E.g., I might want to sort based on the second year, in which case they'd all be sorted based on the re-ordering of $data[1].
I can do this easily with array_multisort:
array_multisort($data[1],SORT_ASC,$data[0],$data[2]);
That's fine, but I don't know how many years of data there will be. I want some way of specifying just the right number of arguments, but I don't know how to do that in php, unless I just have to have an if statement for each possible number of years, which seems incredibly painful:
if ($num_years == 1)
{
array_multisort($data[$which_year],SORT_ASC);
}
else if ($num_years == 2)
{
array_multisort($data[$which_year],SORT_ASC,$data[0],$data[1]); // this does work, interestingly, in spite of the repetition...
}
Anyone know of a better way?
You can always use call_user_func_array, specifying array_multisort as the first parameter and building an array of parameters to be passed to array_multisort. Something like:
$params = array();
foreach($data as $year){
$params[] = $year;
}
call_user_func_array('array_multisort', $params);

Categories