PHP multi-dimensional array find duplicates in specific dimensions - php

I have the following array:
$masterlist=[$companies][$fieldsofcompany][0][$number]
The third dimension only exists if the field selected from $fieldsofcompany = position 2 which contains the numbers array. Other positions contain regular variables. The 3rd dimension is always 0 (the numbers array) or Null. Position 4 contains numbers.
I want to cycle through all companies and remove from the $masterlist all companies which contain duplicate numbers.
My current implementation is this code:
for($i=0;$i<count($masterlist);$i++)
{
if($masterlist[$i][2][0][0] != null)
$id = $masterlist[$i][0];
for($j=0;$j<count($masterlist[$i][2][0]);$j++)
{
$number = $masterlist[$i][2][0][$j];
$query = "INSERT INTO numbers VALUES('$id','$number')";
mysql_query($query);
}
}
Which inserts numbers and associated IDs into a table. I then select unique numbers like so:
SELECT ID,number
FROM numbers
GROUP BY number
HAVING (COUNT(number)=1)
This strikes me as incredibly brain-dead. My question is what is the best way to do this? I'm not looking for code per se, but approaches to the problem. For those of you who have read this far, thank you.

For starters, you should prune the data before sticking it into the database.
Keep a look up table that keeps track of the 'number'.
If the number is not in the look up table then use it and mark it, otherwise if its in the look up table you can ignore it.
Using an array for the look up table and with keys being the 'number' you can use the isset function to test if the number has appeared before or not.
Example pseudo code:
if(!isset($lookupTable[$number])){
$lookupTable[$number]=1;
//...Insert into database...
}

Now that I think I understand what you really want, you might want to stick with your two-pass approach but skip the MySQL detour.
In the first pass, gather numbers and duplicate companies:
$duplicate_companies = array();
$number_map = array();
foreach ($masterlist as $index => $company)
{
if ($company[2][0][0] === null)
continue;
foreach ($company[2][0] as $number)
{
if (!isset($number_map[$number])
{
// We have not seen this number before, associate it
// with the first company index.
$number_map[$number] = $index;
}
else
{
// Both the current company and the one with the index stored
// in $number_map[$number] are duplicates.
$duplicate_companies[] = $index;
$duplicate_companies[] = $number_map[$number];
}
}
}
In the second pass, remove the duplicates we have found from the master list:
foreach (array_unique($duplicate_companies) as $index)
{
unset($masterlist[$index]);
}

Related

Assign random but even amount from an array to a list array in PHP?

I have two arrays. One array $People currently creates number of 44 individuals. Lets just assume currently its
$People = array('1','2',...,'44');.
I have another array of 15 elements.
$Test = array('A','B',...'O');
Now I want to be able to assign the test randomly to each individual. I know how to do this using random function in php.
Where it has got tricky for me and what I need help with is how can I even out the test array. What I mean by this is since there are currently 44 individuals (this array will grow in future), what I want is 14 test versions to have 3 individuals and 1 version would have 2. So I want the test array to even out. I also want it to handle as growth of $People array.
Ex: Test Version D will have individual '4', '25'. Every other version has three random individuals.
Few ideas I came up with are things like running random integer on $Test array and which ever gets highest/lowest gets 2 individuals and rest three. This would give a problem when I increase the size of $People array; to deal with that I though about using modulus to figure out what will be even number of $Test beforehand.
I can do this and other ideas but am pretty sure there has to be a better way to do this.
Clarifying your situation:
You want to distribute the values inside $People randomly amongst your $Test array. The problem you stated you are having is that the amount of values in $People isn't always perfectly dividable by the amount of values in $Test, and you aren't sure how to go about implementing code to distribute the values evenly.
Proposed solution:
You could obtain the values in a foreach loop randomly 1 by 1 from a shuffled version of $People and put them in a new array called $Result. You would also have a conditional checking if you have extracted all the values from the shuffled $People array: if($count>=$arrayCount) where $arrayCount=$count($shuffledPeople);. If you have obtained all the values, you first make the $bool value false (in order not to iterate through the while loop anymore, and then you break; out of the foreach loop.
$Result =[];//the array containing the results
$shuffledPeople = $People;
shuffle($shuffledPeople);//mixing up the array
$arrayCount = count($shuffledPeople);//finding the total amount of people
$count = 0;
$bool = TRUE;
while ($bool)
{
foreach($Test as $value)
{
$Result[$value][] = $shuffledPeople[$count];
$count++;
if ($count>=$arrayCount)
{
$bool = FALSE;
break;
}
}
}
To view the results, all you would need to do is:
foreach ($Result as $key => $value)
{
echo "{$key}: <br>";
if (is_array($value))
{
foreach ($value as $something)
{
echo "-->{$something}<br>";
}
}
else
{
echo "-->{$value}<br>";
}
}
I believe that this is what you want to do...
Assume that you have $people and $test arrays. You want to know how many people per test...
$ppt = ceil(sizeof($people)/sizeof($test));
Now, $ppt is the people per test. The next step is to shuffle up the people so they are randomly assigned to the tests.
shuffle($people);
Now, we can chunk up the people into sub-arrays such that each sub-array is assigned to a test. Remember, the chunks are random now because we shuffled.
$chunks = array_chunk($people, $ppt);
Now, everyone in $chunks[0] will take $test[0]. Everyone in $chunks[1] will take $test[1]. Everyone in $chunks[2] will take $test[2].

Grouping array values by similar

i'm making a grocerylist system that takes into account amount of recipies you throw at it.
based on that, it combines values and outputs a grocerylist for you.
i'm looping a mysql query and create arrays like this:
foreach sql loop{
$ingredients = array(
"id"=>array($id),
"name"=>array($name),
"amount"=>array($amount),
"unit"=>array($unit)
);
foreach ($ingredienser as $key => $value) {
foreach ($value as $index => $v) {
echo $key."=";
echo $v;
echo "<br><hr>";
}
}
}
That whole ordeal outputs a list like this if i throw 1 recipe at it:
id=150
name=Cherrytomater
amount=300
unit=Gram
id=151
name=Kyllingfilet
amount=4
unit=Stykk
if i throw 2 recipies it throws the next recipe under it.
However, i need it to do a certain set of things.
merge values if the name is similar
ignore the above merge if unit is different (its a shopping list after all)
output a combined list of ingredients based on the recipies and amount of recipies i throw at it.
To further explain; the sql equivalent of what im trying to achieve is :
$dupe = "2" //amount of similar recipies
SELECT i.id, i.ingred, r.enhet, r.id, SUM(r.mengde * $dupe) total, r.enhet FROM oppskriftingred r left join ingrediens i on i.id = r.ingrediens WHERE r.oppskriftid IN $vars2 GROUP BY i.ingred, r.enhet ORDER BY i.ingred
However, the sql approach wont work as the SUM doesn't differentiate between different recipies.. if $ dupe is 4 for one recipe and 2 for another, its 4 for both.
So how can i do this in php with arrays? Any help is greatly appreciated!
Your "this is what I need list" has one problematic point:
1. merge value if the name is SIMILAR
The rest is quite simple. You have an array of ingerdients $ingredients. You need to loop through this and compare every entry to a result array, to figure out if your points 2 and 1 match it to a existing or new entry. I would give the new array a key, that makes matching easy. Let's say the unit and the name as a string: 'Gram-Cherrytomater'
$result = array();
foreach ($ingerdients as $item) {
$newKey = $item['unit'].'-'.$item['name'];
if (isset($result[$newKey]) {
// add amount to existing item
$result[$newKey]['amount'] += $item['amount'];
} else {
// add new item
$result[$newKey] = $item;
}
}
// now loop trough the result and display whatever you want
The problem is with the SIMILAR thing. I do not know, how you want to compare to strings that are similar. You should do this when getting the $newKey. Do something with $item['name'] to make it "similar".
You could use some php function like similar_text(), levenshtein() or soundex(). I do not know how good this works for your language (Danish?).

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.

PHP Grid type Array get row or column

This may sound like a silly question and I'm not thinking hard enough,
Or its harder than i think...
Say i have a array of numbers like:
$table = array(
'5','2','1','4','4','4',
'1','2','4','2','1','1',
'3','4','3','1','4','4',
'1','4','2','S','4','4',
'1','2','4','2','1','1',
'5','2','6','4','8','1'
);
S = where I want to get either the row or column based on where "S" is.
I know i can get where S is.
by doing:
$start = array_search('S', $table);
The ''grid'' this array is based on,The S can can be anywhere.
And the grid itself can be different sizes length and width.
How would i go about getting the whole row or column.?
(IE:S is in column 3 : 4,2,1,S,2,4)
(IE:S is in row 3 : 1,4,2,S,4,4)
Just a hint in the right direction would be helpful, Don't need to go all out for me.
Or a idea on how to approach this.
Okay, I guess you have the width you want to use stored somewhere! (And you don't want to use 2-dimensional arrays, check the other answer if you don't care, a 2-dimensional array makes a lot more sense in this case).
If you want to get the line, use $start which is the position of S in the array and divide it by the width. Then you need to round down or up (depends on if you are starting to count at 0 or 1)
For column you need to do something similar, use $start % $width here. (add 1 if you start counting at 1).
Here is a hint:
$table[] = array();
$table[][0] = array(5,2,1,4,5);
$table[][1] = array(1,5,2,3,3);
$table[][2] = array(1,2,'s',4,5);
You will need an array of arrays to have an x/y search.
Your x is the nth element in the outer array.
Your y is the nth element in the inner array.
You can search your string like 's' in array and find column and row of that :
$cols=5; // your columns count
$row=1;
$i=1;
$array = array('1','2','3','4','5','6','7','s','9');
foreach($array as $value)
{
if($value=='s')
{
echo 'Column : '.$i .' || Row : ' .$row ;
}
if($i==$cols)
{
$i=0;
$row++;
}
$i++;
}

Filtering through several SQL searches to build a single array - need pointers

I'm new to web developing.
This is part of a phone service, and I'm trying to filter through 3 different arrays that are filled with strings from three database searches: $sfaa, $sfipc, and $sfuaa. I have to filter the three database arrays to locate available customer service agents. The output would be an array filled with the IVR_Number to dial.
Heres an example of the string: "'Id', 'IVR_Number', 'Market_Id'"
I have to explode the string in order to get my data from each value in the arrays. Then based on a one-to-many id in each string I have to check if the id from $sfaa is in $sfipc or $sfuaa. If not then I have to build an array with the filtered records, from there I have to locate a value from the exploded string in $sfaa that belongs to that id. I wrote the following code but theres got to be an easier way?? I hope.... The client has to wait for these results before moving forward. There is usually only 10 or 15 records.
This code works I'm just wondering if there is an easier way to do this
Any tips
// formalua needed to filter above results and fill $aadl array
// explode each active agent array
$activeagentsfec=0;
$aaivra= array();
$aaida= array();
foreach ($sfaa as $aavalue)
{
${'aadetails'.$activeagentsfec} = explode("'",$aavalue);
${'aaivr'.$activeagentsfec} = ${'aadetails'.$activeagentsfec}[5];
${'aaid'.$activeagentsfec} = ${'aadetails'.$activeagentsfec}[1];
array_push($aaivra, ${'aaivr'.$activeagentsfec});
array_push($aaida,${'aaid'.$activeagentsfec});
$activeagentsfec++;
}
// explode each inprogress call array
$activecallsfec=0;
$actida= array();
$acfida= array();
foreach ($sfipc as $acvalue)
{
${'acdetails'.$activecallsfec} = explode("'",$acvalue);
${'actid'.$activecallsfec} = ${'acdetails'.$activecallsfec}[5];
${'acfid'.$activecallsfec} = ${'acdetails'.$activecallsfec}[7];
array_push($actida, ${'actid'.$activecallsfec});
array_push($acfida, ${'acfid'.$activecallsfec});
$activecallsfec++;
}
// explode each unvailable agent
$unavailableagentsfec=0;
$uaaida= array();
foreach ($sfuaa as $uavalue)
{
${'uadetails'.$unavailableagentsfec} = explode("'",$uavalue);
${'uaaid'.$unavailableagentsfec} = ${'uadetails'.$unavailableagentsfec}[3];
array_push($uaaida, ${'uaaid'.$unavailableagentsfec});
$unavailableagentsfec++;
}
// create available agent array by id
$aaafec=0;
$aada= array();
foreach ($aaida as $aaidavalue)
{
if (in_array($aaidavalue,$actida,true))
$aaafec++;
elseif(in_array($aaidavalue,$acfida,true))
$aaafec++;
elseif(in_array($aaidavalue,$uaaida,true))
$aaafec++;
else
array_push($aada, $aaidavalue);
}
// available agent arry by ivr
$aadl= array();
foreach ($aada as $aadavalue)
{
$aaaivrsv= array_search($aadavalue,$aaida,true);
array_push($aadl,$aaivra[$aaaivrsv]);
}
Given what you were saying in the comments, I'll try to give you some useful thoughts...
You carry out much the same process to parse $sfaa, $sfipc, and $sfuaa - explode, get certain columns. If you had some way to abstract that process, with a generic function for the parsing, that returns the data in a better format, called three times on each array, you'd see better through your code.
In the same way, your process is tightly coupled to the current state of the data - e.g. ${'acdetails'.$activecallsfec}[5]; is your fifth item today, but will it always be? Something generic, where you seek an column by name, might save you a lot of trouble...
finally, when merging data, if the data is sorted before hand the merge can be a lot quicker - seeking N items in a list of M, with an unsorted list takes O(n*m) operations, but if both are sorted it's O(min(m,n)).
I've taken the time to go through your code... Unless you're usign some of its variables elsewhere, here is a shorter equivalent:
// formula needed to filter above results and fill $aadl array
// explode each active agent array
$aaivra= array();
$aaida= array();
foreach ($sfaa as $aavalue)
{
$a = explode("'",$aavalue);
array_push($aaivra, $a[5]);
array_push($aaida,$a[1]);
}
// explode each inprogress call array
$actida= array();
$acfida= array();
foreach ($sfipc as $acvalue)
{
$a = explode("'",$acvalue);
array_push($actida, $a[5]);
array_push($acfida, $a[7]);
}
// explode each unvailable agent
$uaaida= array();
foreach ($sfuaa as $uavalue)
{
$a= explode("'",$uavalue);
array_push($uaaida, $a[3]);
}
// create available agent array by id
$aada= array();
foreach ($aaida as $aaidavalue)
{
if (!in_array($aaidavalue,$actida,true) &&
!in_array($aaidavalue,$acfida,true) &&
!in_array($aaidavalue,$uaaida,true))
array_push($aada, $aaidavalue);
}
// available agent arry by ivr
$aadl= array();
foreach ($aada as $aadavalue)
{
$aaaivrsv= array_search($aadavalue,$aaida,true);
array_push($aadl,$aaivra[$aaaivrsv]);
}

Categories