0

I have parsed three different text files:

space.txt

Kournikova Anna F F 6-3-1975 Red
Hingis Martina M F 4-2-1979 Green
Seles Monica H F 12-2-197

comma.txt

Abercrombie, Neil, Male, Tan, 2/13/1943
Bishop, Timothy, Male, Yellow, 4/23/1967
Kelly, Sue, Female, Pink, 7/12/1959

pipe.txt

Smith | Steve | D | M | Red | 3-3-1985
Bonk | Radek | S | M | Green | 6-3-1975
Bouillon | Francis | G | M | Blue | 6-3-1975

I used the following code to parse all files into one array .............

<?php 

    $space_txt = './data/input/space.txt';
    $comma_txt = './data/input/comma.txt';
    $pipe_txt = './data/input/pipe.txt';

    $parsed_space_data = file_get_contents($space_txt);
    $parsed_comma_data = file_get_contents($comma_txt);
    $parsed_pipe_data = file_get_contents($pipe_txt);



    $space_array = myExpldeLoopFunc("space"," ", $parsed_space_data);
    $comma_array = myExpldeLoopFunc("comma",",", $parsed_comma_data);
    $pipe_array = myExpldeLoopFunc("pipe"," | ", $parsed_pipe_data);


    $finalArray = array_merge($space_array, $comma_array, $pipe_array);


    function changeGender($gender) {

        if($gender === 'F') {
            return str_replace('F', 'Female', $gender);
        }

        elseif($gender === 'M') {
            return str_replace('M', 'Male', $gender);
        }
    }

    function normalizeDate($date) {
        return str_replace('-', '/', $date);
    }









    function myExpldeLoopFunc($name, $sep, $data) {

        $parsedData = explode("\r", $data);




        $arr = [];
        foreach ($parsedData as $data) {
            $data_arr = explode($sep, $data);





            if($name == 'space'){

                $arr[] = [
                    "last_name" => $data_arr[0],
                    "first_name" => $data_arr[1],
                    // "middle_initial" => $data_arr[2],
                    "gender" => changeGender($data_arr[3]),
                    "date_of_birth" => normalizeDate($data_arr[4]),
                    "favorite_color" => $data_arr[5]



                ];








            }

                elseif($name == 'comma') {
                    $arr[] = [
                    "last_name" => $data_arr[0],
                    "first_name" => $data_arr[1],
                    "gender" => $data_arr[2],
                    "date_of_birth" => normalizeDate($data_arr[4]),
                    "favorite_color" => $data_arr[3]

                    ];
                }

            elseif ($name == 'pipe') {
                    $arr[] = [
                    "last_name" => $data_arr[0],
                    "first_name" => $data_arr[1],
                    // "middle_initial" => $data_arr[2],
                    "gender" => changeGender($data_arr[3]),
                    "date_of_birth" => normalizeDate($data_arr[5]),
                    "favorite_color" => $data_arr[4]



                ];


            }





    }



    return $arr;







        }








for ($i=0; $i < count($finalArray); $i++) {


foreach ($finalArray as $key => $row) {
$gender[$key] = $row['gender'];
$last_name[$key] = $row['last_name'];
}

array_multisort($gender, SORT_ASC, $last_name, SORT_ASC, $finalArray);

echo join(' ',  $finalArray[$i]) . '<br>';


    }

var_dump($finalArray);






 ?>

Now I have the following array ...........

array (size=9)
  0 => 
    array (size=5)
      'last_name' => string 'Kournikova' (length=10)
      'first_name' => string 'Anna' (length=4)
      'gender' => string 'Female' (length=6)
      'date_of_birth' => string '6/3/1975' (length=8)
      'favorite_color' => string 'Red' (length=3)
  1 => 
    array (size=5)
      'last_name' => string '
Hingis' (length=7)
      'first_name' => string 'Martina' (length=7)
      'gender' => string 'Female' (length=6)
      'date_of_birth' => string '4/2/1979' (length=8)
      'favorite_color' => string 'Green' (length=5)
  2 => 
    array (size=5)
      'last_name' => string '
Seles' (length=6)
      'first_name' => string 'Monica' (length=6)
      'gender' => string 'Female' (length=6)
      'date_of_birth' => string '12/2/1973' (length=9)
      'favorite_color' => string 'Black' (length=5)
  3 => 
    array (size=5)
      'last_name' => string 'Abercrombie' (length=11)
      'first_name' => string ' Neil' (length=5)
      'gender' => string ' Male' (length=5)
      'date_of_birth' => string ' 2/13/1943' (length=10)
      'favorite_color' => string ' Tan' (length=4)
  4 => 
    array (size=5)
      'last_name' => string '
Bishop' (length=7)
      'first_name' => string ' Timothy' (length=8)
      'gender' => string ' Male' (length=5)
      'date_of_birth' => string ' 4/23/1967' (length=10)
      'favorite_color' => string ' Yellow' (length=7)
  5 => 
    array (size=5)
      'last_name' => string '
Kelly' (length=6)
      'first_name' => string ' Sue' (length=4)
      'gender' => string ' Female' (length=7)
      'date_of_birth' => string ' 7/12/1959' (length=10)
      'favorite_color' => string ' Pink' (length=5)
  6 => 
    array (size=5)
      'last_name' => string 'Smith' (length=5)
      'first_name' => string 'Steve' (length=5)
      'gender' => string 'Male' (length=4)
      'date_of_birth' => string '3/3/1985' (length=8)
      'favorite_color' => string 'Red' (length=3)
  7 => 
    array (size=5)
      'last_name' => string '
Bonk' (length=5)
      'first_name' => string 'Radek' (length=5)
      'gender' => string 'Male' (length=4)
      'date_of_birth' => string '6/3/1975' (length=8)
      'favorite_color' => string 'Green' (length=5)
  8 => 
    array (size=5)
      'last_name' => string '
Bouillon' (length=9)
      'first_name' => string 'Francis' (length=7)
      'gender' => string 'Male' (length=4)
      'date_of_birth' => string '6/3/1975' (length=8)
      'favorite_color' => string '

Blue' (length=4)

So Far the output is ........

Kelly Sue Female 7/12/1959 Pink
Bishop Timothy Male 4/23/1967 Yellow
Abercrombie Neil Male 2/13/1943 Tan
Hingis Martina Female 4/2/1979 Green
Seles Monica Female 12/2/1973 Black
Kournikova Anna Female 6/3/1975 Red
Bonk Radek Male 6/3/1975 Green
Bouillon Francis Male 6/3/1975 Blue
Smith Steve Male 3/3/1985 Red

I want to sort the array by Females then Males, then by last_name asc ........

Hingis Martina Female 4/2/1979 Green
Kelly Sue Female 7/12/1959 Pink
Kournikova Anna Female 6/3/1975 Red
Seles Monica Female 12/2/1973 Black
Abercrombie Neil Male 2/13/1943 Tan
Bishop Timothy Male 4/23/1967 Yellow
Bonk Radek Male 6/3/1975 Green
Bouillon Francis Male 6/3/1975 Blue
Smith Steve Male 3/3/1985 Red

I also tried ......

function sortBy($field, &$array, $direction = 'asc') { 
usort($array, create_function('
$a, $b', ' $a = $a["' . $field . '"]; 
$b = $b["' . $field . '"]; 
if ($a == $b) { 
return 0; 
} 
return ($a ' . ($direction == 'desc' ? '>' : '<') .' $b) ? -1 : 1; ')); 
return true; 
} 

for ($i=0; $i < count($finalArray); $i++) { 
sortBy('gender', $finalArray); 
sortBy('last_name', $finalArray); 
echo join(' ', $finalArray[$i]) . '<br>'; 
}

I have tried array_multisort(), usort(), sort(), asort(), and I still couldn't produce the results I wanted. What solution can be used to produce the outcome?

halfer
  • 19,824
  • 17
  • 99
  • 186
Michael Stokes
  • 401
  • 1
  • 10
  • 24
  • 1
    `usort` is still your solution. Show what you tried with it – u_mulder Jun 26 '16 at 09:32
  • `function sortBy($field, &$array, $direction = 'asc') { usort($array, create_function('$a, $b', ' $a = $a["' . $field . '"]; $b = $b["' . $field . '"]; if ($a == $b) { return 0; } return ($a ' . ($direction == 'desc' ? '>' : '<') .' $b) ? -1 : 1; ')); return true; } ` `for ($i=0; $i < count($finalArray); $i++) { sortBy('gender', $finalArray); sortBy('last_name', $finalArray); echo join(' ', $finalArray[$i]) . '
    '; }`
    – Michael Stokes Jun 26 '16 at 09:40
  • 3
    Edit your question and add that to it rather then putting it on a comment! – Andrew Willis Jun 26 '16 at 09:52
  • I've already included the code from the comment. What should my question be edited to? – Michael Stokes Jun 26 '16 at 09:54
  • You need to write an user defined sorting routine, using a function like `usort()`, that way you can sort by two, or more, criteria. See: http://php.net/manual/en/function.usort.php – KIKO Software Jun 26 '16 at 09:59
  • I've tried usort() in so many ways and still couldn't produce the results. – Michael Stokes Jun 26 '16 at 10:00
  • I could help you, but I don't feel like rewriting your array dump into a real, and usable, array. Your question could be more to the point, I'm afraid. – KIKO Software Jun 26 '16 at 10:01
  • What do you mean more to the point? I've tried to make my question as clear as possible. – Michael Stokes Jun 26 '16 at 10:03
  • You need to sort on two criteria, your question could be about only that. I'm working on an example answer. – KIKO Software Jun 26 '16 at 10:05
  • I don't see how this can help me come up with a solution. What am I suppose to do about my code? – Michael Stokes Jun 26 '16 at 10:06
  • I didn't see your edited comment – Michael Stokes Jun 26 '16 at 10:08

2 Answers2

1

Ok, I'll give an example, with two criteria, but it's untested on your data. So the compare() function below receives two arrays to compare. How to do that? Each array, in the example, contains two numbers, first we sort on the number with key = 0 and then on the number with key = 1. Your keys are, of course, different.

function compare($array1,$array2)
{
  // numbers at key 0 are equal
  if ($array1[0] == $array2[0])
  {
    // so we look at key 1.
    if ($array1[1] == $array2[1]) return 0;
    return ($array1[1] < $array2[1]) ? -1 : 1;
  }
  return ($array1[0] < $array2[0]) ? -1 : 1;
}

$a = [[1,2],[3,4],[5,4],[4,2],[1,8]];

usort($a,'compare');

foreach ($a as $key => $value)
{
  echo "<pre>$key: ".print_r($value,TRUE)."\n</pre>";
}

All you need to do is to adapt this to your case.

This sorts on two values in an array, both, as you would call it, ascending. Just change the < to > if you want one of them in decending order. The keys used here are 0 and 1, your keys are like gender and last_name.

Some values cannot be compared with the < comparison operator, so you would need to use something else. In case of the last_name you might want to use strcasecmp().

KIKO Software
  • 15,283
  • 3
  • 18
  • 33
  • So far some of the values had duplicated. I have used `gender` and `last_name` but used `$finalArray` as $a – Michael Stokes Jun 26 '16 at 10:37
  • Here are the results ............ `Smith Steve Male 3/3/1985 Red Abercrombie Neil Male 2/13/1943 Tan Smith Steve Male 3/3/1985 Red Abercrombie Neil Male 2/13/1943 Tan Bishop Timothy Male 4/23/1967 Yellow Bonk Radek Male 6/3/1975 Green Kournikova Anna Female 6/3/1975 Red Bonk Radek Male 6/3/1975 Green Kournikova Anna Female 6/3/1975 Red` – Michael Stokes Jun 26 '16 at 10:48
  • That doesn't look very sorted to me. – KIKO Software Jun 26 '16 at 11:04
  • Didn't work. I don't understand why some of the values had been repeated. – Michael Stokes Jun 26 '16 at 11:05
  • The example works, your application to your own data is flawed in some way. Why not ask a new question, write out an example input array, like I did, but with your own data, and ask how to apply usort with two criteria? – KIKO Software Jun 26 '16 at 11:07
  • Just did. Please see it at http://stackoverflow.com/questions/38038026/php-how-to-apply-usort-with-2-criteria – Michael Stokes Jun 26 '16 at 11:26
  • Ah, yes. You did indeed. – KIKO Software Jun 26 '16 at 11:30
  • You suggested I asked a new question using my data how to apply usort() using criteria and I did. What did you mean I didn't? Are you saying I have to come up with a new line of code to combine all my txt files into one and start over again from scratch? – Michael Stokes Jun 26 '16 at 11:32
  • Oh I'm sorry just saw your new comment. – Michael Stokes Jun 26 '16 at 11:33
  • The OP's problem is not with `array_multisort`: that is the right way to do this. No need to do `usort`. The problem is elsewhere. – trincot Jun 26 '16 at 11:34
0

The main issues in your code:

  • You split by \r: it is better to split by \n which works on all platforms. But since a line-feed can be \r\n, you need to remove that other character as well. This you can do with trim(). So I would suggest to apply trim() to all values.
  • The changeGender function looks weird. You can do all that with: return $gender === 'F' ? 'Female' : 'Male'.
  • You might also want to skip empty lines in your input.
  • You do your sorting in a loop on your array, that is wrong. That outer loop should be removed. If you need it for listing the data, then move the sorting out of it.

The corrected code (and also nicely indented) follows with my comments marked as //**:

$space_txt = './data/input/space.txt';
$comma_txt = './data/input/comma.txt';
$pipe_txt = './data/input/pipe.txt';

$parsed_space_data = file_get_contents($space_txt);
$parsed_comma_data = file_get_contents($comma_txt);
$parsed_pipe_data = file_get_contents($pipe_txt);

$space_array = myExpldeLoopFunc("space", " ", $parsed_space_data);
$comma_array = myExpldeLoopFunc("comma", ",", $parsed_comma_data);
$pipe_array = myExpldeLoopFunc("pipe", " | ", $parsed_pipe_data);

$finalArray = array_merge($space_array, $comma_array, $pipe_array);

function changeGender($gender) {
    return $gender === 'F' ? 'Female' : 'Male'; //** This is more straightforward
}

function normalizeDate($date) {
    return str_replace('-', '/', $date);
}

function myExpldeLoopFunc($name, $sep, $data) {
    $parsedData = explode("\n", $data); //** use "\n" instead of "\r"
    $arr = [];
    foreach ($parsedData as $data) {
        if ($data === "") continue; //** skip empty lines
        $data_arr = explode($sep, $data);
        if($name == 'space'){
            $arr[] = [
                "last_name" => trim($data_arr[0]), //** trim all elements (also removes "\r")
                "first_name" => trim($data_arr[1]),
                // "middle_initial" => trim($data_arr[2]),
                "gender" => changeGender(trim($data_arr[3])),
                "date_of_birth" => normalizeDate(trim($data_arr[4])),
                "favorite_color" => trim($data_arr[5])
            ];
        } elseif($name == 'comma') {
            $arr[] = [
                "last_name" => trim($data_arr[0]),
                "first_name" => trim($data_arr[1]),
                "gender" => trim($data_arr[2]),
                "date_of_birth" => normalizeDate(trim($data_arr[4])),
                "favorite_color" => trim($data_arr[3])
            ];
        } elseif ($name == 'pipe') {
            $arr[] = [
                "last_name" => trim($data_arr[0]),
                "first_name" => trim($data_arr[1]),
                // "middle_initial" => trim($data_arr[2]),
                "gender" => changeGender(trim($data_arr[3])),
                "date_of_birth" => normalizeDate(trim($data_arr[5])),
                "favorite_color" => trim($data_arr[4])
            ];
        }
    }
    return $arr;
}

//** Removed the bad for-loop that appeared here.
foreach ($finalArray as $key => $row) {
    $gender[$key] = $row['gender'];
    $last_name[$key] = $row['last_name'];
}
array_multisort($gender, SORT_ASC, $last_name, SORT_ASC, $finalArray);

//** Output in a loop, but leave the above sorting out of it 
foreach ($finalArray as $row) {
    echo join(' ',  $row) . "<br>\n";
}

print_r($finalArray);

See it run on eval.in which uses the sample data you provided.

The order is as desired.

trincot
  • 317,000
  • 35
  • 244
  • 286
  • Yes, but it looks weird. It is unnecessarily complicated. – trincot Jun 26 '16 at 11:37
  • I understand what you're saying but as long it's working. I'll try your suggested code. – Michael Stokes Jun 26 '16 at 11:39
  • The main issue with your code is that you do not trim the "\n" and other white-space that will badly influence the sorting. Also the loop near the end makes no sense, although it would not change the sort order in a bad way. – trincot Jun 26 '16 at 11:40
  • The loop is used to list the data in the following format above. – Michael Stokes Jun 26 '16 at 11:43
  • OK, but if you need to list the data, then you should do that in a separate loop, not in a loop where you do the sorting. The sorting should happen outside of such a loop. – trincot Jun 26 '16 at 11:52
  • I added code in my answer which does a loop to output the list. – trincot Jun 26 '16 at 11:57