统一排列/分配数组项

时间:2017-02-09 22:27:33

标签: php arrays sorting math multidimensional-array

我有一个带有type属性的多维关联数组。它看起来像这样:

$data = array(
  array( "name" => "SomeName", "type" => "A"),
  array( "name" => "SomeName", "type" => "A"),
  array( "name" => "SomeName", "type" => "A"),
  array( "name" => "SomeName", "type" => "A"),
  array( "name" => "SomeName", "type" => "A"),
  array( "name" => "SomeName", "type" => "B"),
  array( "name" => "SomeName", "type" => "B"),
  array( "name" => "SomeName", "type" => "B"),
  array( "name" => "SomeName", "type" => "C"),
  array( "name" => "SomeName", "type" => "C")
);

我想重新排列它以使项目分布更均匀(如果可能,重复类型最少)。它应该是这样的:

array(
  array( "name" => "SomeName", "type" => "A"),
  array( "name" => "SomeName", "type" => "B"),
  array( "name" => "SomeName", "type" => "A"),
  array( "name" => "SomeName", "type" => "C"),
  array( "name" => "SomeName", "type" => "A"),
  array( "name" => "SomeName", "type" => "B"),
  array( "name" => "SomeName", "type" => "A"),
  array( "name" => "SomeName", "type" => "C"),
  array( "name" => "SomeName", "type" => "A"),
  array( "name" => "SomeName", "type" => "B")
);

我到目前为止所做的是找到每种类型和总数的计数:

$count_a = 5;
$count_b = 3;
$count_c = 2;
$total = 10;

以及每种类型的比率:

$ratio_a = 0.5; //(5/10)
$ratio_b = 0.3; //(3/10)
$ratio_c = 0.2; //(2/10)

我只是被困在这里。我应该尝试使用数字创建一个新属性index,然后根据它进行排序吗?或者也许以某种方式使用模运算符?我还尝试将这些项目分成3个不同的数组,如果这样可以更容易。

10 个答案:

答案 0 :(得分:7)

这是一种尽可能避免重复模式的解决方案。

对于AAAAABBBCC,它会生成ABABABACAC;

对于AAAAABBBCCC,它会生成ABCABABACAC;

除了按类型计数排序外,它还以线性时间运行(它接受未排序的数据数组)。结果在$distributed_data。有关解释,请参阅下文。

代码

$data = array(
  array( "name" => "SomeName", "type" => "A"),
  array( "name" => "SomeName", "type" => "A"),
  array( "name" => "SomeName", "type" => "A"),
  array( "name" => "SomeName", "type" => "B"),
  array( "name" => "SomeName", "type" => "B"),
);

$distributed_data = array();
$counts = array();
$size = sizeof($data);

// Count values
foreach ($data as $entry) {
  $counts[$entry["type"]] = isset($counts[$entry["type"]]) ? $counts[$entry["type"]] + 1 : 1;
}

// Set counter
for ($i = 0; $i < $size; $i++) {
  $data[$i]["count"] = $counts[$data[$i]["type"]];
}

// Sort by count
usort($data, function($entry1, $entry2) {
    return $entry2["count"] <=> $entry1["count"];
});

// Generate the distributed array
$max_length = $data[0]["count"];
$rows = ceil($size / $max_length);
$last_row = ($size - 1) % $max_length + 1;
$row_cycle = $rows;

$row = 0;
$col = 0;
for ($i = 0; $i < $size; $i++) {
  if ($i == $rows * $last_row) {
    $row_cycle -= 1;
  }

  $distributed_data[$i] = $data[$row * $max_length + $col];

  $row = ($row + 1) % $row_cycle;
  if ($row == 0) {
    $col++;
  }
}

说明

首先,根据每种类型的重复次数对条目进行排序。例如。 CBBCAAB变为BBBAACC

然后想象一个表中列数最多的表(例如,如果你有AAAABBCC,最常见的是4,表格将有4列。)

然后将所有条目写入表格,从左到右,根据需要跳转到新行。

E.g。对于AAAAABBBCCC,你会得到一张这样的表:

Table example

要生成最终的分布式数组,只需从上到下读取条目,然后根据需要转移到新列。

在上面的示例中,您将获得ABCABABACAC

获得重复条目的唯一方法是在列中包含两个相同的字符,或者在转移到右侧的列时遇到相同的字符。

第一种情况不可能发生,因为角色群需要环绕而且这种情况不会发生,因为没有字符组长于列数(这就是我们的方式)定义了表格。

第二种情况只有在第二行不满时才会发生。例如。 AAAABB留下第二行有两个空单元格。

答案 1 :(得分:4)

您应该对已排序的类型进行排序,并按顺序逐步更改所选类型。

$data = array(
  array( "name" => "SomeName1", "type" => "A"),
  array( "name" => "SomeName2", "type" => "A"),
  array( "name" => "SomeName3", "type" => "A"),
  array( "name" => "SomeName4", "type" => "A"),
  array( "name" => "SomeName5", "type" => "A"),
  array( "name" => "SomeName6", "type" => "B"),
  array( "name" => "SomeName7", "type" => "B"),
  array( "name" => "SomeName8", "type" => "B"),
  array( "name" => "SomeName9", "type" => "C"),
  array( "name" => "SomeName0", "type" => "C")
);

$dataSorted = array();
$counts = array();

foreach($data as $elem) {
    // just init values for a new type
    if(!isset($counts[$elem['type']])) {
        $counts[$elem['type']] = 0;
        $dataByType[$elem['type']] =  array();
    }

    // count types
    $counts[$elem['type']]++;

    // save it to grouped array
    $dataByType[$elem['type']][] =  $elem;
}

// sort it to A=>5, B=>3 C=>2
arsort($counts, SORT_NUMERIC);

// get sorted types as an array
$types = array_keys($counts);

// index will be looped 0 -> count($types) - 1 and then down to 0 again
$currentTypeIndex = 0;

// make a walk on sorted array. First get the most popular, then less popular etc.
// when all types are added, repeat
while(count($dataSorted) < count($data)) {
    $currentType = $types[$currentTypeIndex];

    // skip adding if we ran out this type
    if($counts[$currentType]) {
        // pop an element of selected type
        $dataSorted[] = array_pop($dataByType[$currentType]);

        // decrease counter
        $counts[$currentType]--;
    }

    // choose next type
    $currentTypeIndex = (++$currentTypeIndex)%count($types);
}

print_r($dataSorted);

代码按ABCABCABAA的顺序输出元素。

UPD。在count(maxtype)&gt;的情况下发生尾随加倍count(nexttype) + 1

答案 2 :(得分:4)

算法:

function distribute($data) {
    $groups = [];
    foreach ($data as $row) {
        $groups[$row['type']][] = $row;
    }
    $groupSizes = array_map('count', $groups);
    asort($groupSizes);

    $result = [];
    foreach ($groupSizes as $type => $groupSize) {
        if (count($result) == 0) {
            $result = $groups[$type];
        } elseif (count($result) >= count($groups[$type])) {
            $result = merge($result, $groups[$type]);
        } else {
            $result = merge($groups[$type], $result);
        }
    }
    return $result;
}

function merge($a, $b) {
    $c1 = count($a);
    $c2 = count($b);
    $result = [];
    $i1 = $i2 = 0;
    while ($i1 < $c1) {
        $result[] = $a[$i1++];
        while ($i2 < $c2 && ($i2+1)/($c2+1) < ($i1+1)/($c1+1)) {
            $result[] = $b[$i2++];
        }
    }
    return $result;
}

主要思想是将数据拆分成组并将下一个最小的组合并到结果中(从空结果开始)。

在合并两个数组时,项目按浮动键排序,浮点键在此行中计算(在流程上)

while ($i2 < $c2 && ($i2+1)/($c2+1) < ($i1+1)/($c1+1))

作为

floatKey = (index + 1) / (groupSize + 1)

(但这部分可以改进,因此与角落的距离为01),这两个角落的距离是两个项目之间的距离的一半。

首先是来自较大群体的项目。

示例:合并AAAABB A的密钥为0.2, 0.4, 0.6, 0.8 - B 0.33, 0.66。结果将是

A(0.2), B(0.33), A(0.4), A(0.6), B(0.66), A(0.8)

试验:

$testData = [
    'AAAAABBBCC',
    'AAAAABBBCCC',
    'ABBCCC',
    'AAAAAABBC',
    'AAAAAABBBBCCD',
    'AAAAAAAAAABC',
    'hpp',
    'stackoverflow',
    'ACCD', // :-)
];

$results = [];

foreach ($testData as $dataStr) {
    $a = str_split($dataStr);
    $data = [];
    foreach ($a as $type) {
        $data[] = ['type' => $type];
    }
    $result = distribute($data);
    $resultStr = implode(array_column($result, 'type'));
    $results[$dataStr] = $resultStr;
}
var_export($results);

测试结果:

'AAAAABBBCC' => 'BACABACABA',
'AAAAABBBCCC' => 'CABACABACAB',
'ABBCCC' => 'BCACBC',
'AAAAAABBC' => 'ABAACAABA',
'AAAAAABBBBCCD' => 'BACABADABACAB',
'AAAAAAAAAABC' => 'AAACAAAABAAA',
'hpp' => 'php',
'stackoverflow' => 'sakeofwlrovct',
'ACCD' => 'ACDC',

测试演示:http://rextester.com/BWBD90255

您可以轻松地向演示中添加更多测试用例。

答案 3 :(得分:3)

$data = array(
  array( "name" => "SomeName", "type" => "A"),
  array( "name" => "SomeName", "type" => "A"),
  array( "name" => "SomeName", "type" => "A"),
  array( "name" => "SomeName", "type" => "A"),
  array( "name" => "SomeName", "type" => "A"),
  array( "name" => "SomeName", "type" => "B"),
  array( "name" => "SomeName", "type" => "B"),
  array( "name" => "SomeName", "type" => "B"),
  array( "name" => "SomeName", "type" => "C"),
  array( "name" => "SomeName", "type" => "C")
);

//make seperate arrays
echo "<pre>";
foreach($data as $val){

    ${$val["type"]}[]=$val["name"];
    $types[]=$val['type'];
}

$types=array_unique($types);

//make ratio
foreach($types as $val){
    $cnt[]=count($$val);
}
//find maximum from ratio
echo $max=max($cnt);
echo $min=min($cnt);


for($i=0;$i<$max;$i++){
    foreach($types as $val){

            if(isset($$val[$i])){
                $new_array[]=array("name"=>$$val[$i],"type"=>$val);
            }
        }
}

print_r($new_array);

小提琴:http://phpfiddle.org/main/code/ju2k-abte

解释

     - Step 1: Make separate array   

     - Step 2: Count all array and find out the ratio



     - Step 3: Iterate with array with maximum ratio value
     - Step 4: Make array with same index together  and merge them in multidimensional
       array

答案 4 :(得分:2)

检查您想要的确切输出

$data = array(
    array("name" => "SomeName", "type" => "A"),
    array("name" => "SomeName1", "type" => "A"),
    array("name" => "SomeName2", "type" => "A"),
    array("name" => "SomeName3", "type" => "A"),
    array("name" => "SomeName4", "type" => "A"),
    array("name" => "SomeName5", "type" => "B"),
    array("name" => "SomeName6", "type" => "B"),
    array("name" => "SomeName7", "type" => "B"),
    array("name" => "SomeName8", "type" => "C"),
    array("name" => "SomeName9", "type" => "C"),
);
// getting all counts
$type = [];
foreach ($data as $key => $value) {
    if (empty($type) || $type != $value['type']) {
        $type    = $value['type'];
        $counter = 0;
    }
    $temp[$value['type']] = ++$counter;
}
/**
 * array search with multiple values
 *
 * @param  array  $parents  input array
 * @param  array  $searched search array
 *
 * @return int    key of found items
 */
function multidimensional_search($parents, $searched)
{
    if (empty($searched) || empty($parents)) {
        return false;
    }
    foreach ($parents as $key => $value) {
        $exists = true;
        foreach ($searched as $skey => $svalue) {
            $exists = ($exists && isset($parents[$key][$skey]) && $parents[$key][$skey] == $svalue);
        }
        if ($exists) {return $key;}
    }
    return false;
}
$output_array = [];
$first_value  = current($temp);
$first_key    = key($temp);
$flag         = 0;
$junkArr      = array_column($data, 'type', 'name');
$remember_me  = 0;
$incr         = 0;
end($temp);
$end_item = key($temp);
reset($temp);
$remember_index = 0;
for ($i = 0; $i < count($data); $i++) {
    $output_array[] = $data[multidimensional_search($data, ['name' => key($junkArr), 'type' => current($junkArr)])];
    if ($temp[$first_key] > 0) {
        $temp[$first_key] = --$first_value;
    }
    $direction = (empty($direction) || $direction == 'reverse' ? "forward" : "reverse");
    for ($k = 0; $k <= $remember_me; $k++) {
        if ($direction == 'forward') {
            next($temp);
        } else {
            prev($temp);
            if ($k == 0) {
                $incr = $remember_me + 1;
            }
        }
    }
    $remember_me = $incr;
    if ($remember_me == count($temp) - 1) {
        $remember_me = 0;
    }
    $first_key   = key($temp);
    $first_value = current($temp);
    if (in_array($first_key, $junkArr)) {
        $saved_key = key($junkArr);
        reset($junkArr);
        while ($first_key !== current($junkArr)) {
            next($junkArr);
        }
        unset($junkArr[$saved_key]);
    }
}
pr($output_array);
die;

以您喜欢的方式映射。

尝试一下,它会起作用。

我想做的是,

  1. 获取所有类型的所有计数器
  2. 然后我用名称和类型映射它,以便我们可以通过名称
  3. 进行唯一识别
  4. 然后我在temp上使用指针变量来跟踪每个类型左边的w.r.t计数
  5. 我从顶部到底部为每种类型填充具有唯一键值对的输出数组。
  6. 我使用junkarray在剩余计数的帮助下移动指针。

答案 5 :(得分:2)

你可以这样使用

{project}\obj\local\armeabi\

谢谢,

答案 6 :(得分:0)

如果您想要的是重新排序列表以最小化重复次数,那么一旦您拥有每种类型的计数,例如

$count_a = 5;
$count_b = 3;
$count_c = 2;
$total = 10;

然后您可以选择人口最多的类型,并从该类型中选择一行,并将其放在您正在创建的列表中的第0位。然后减少该类型的计数,并从未选择的人口最多的类型中进行选择。继续,直到只剩下一种类型,并将它们放在列表的末尾。

答案 7 :(得分:0)

这是基于naktinis's great answer上的想法的另一个实现:

// split data into arrays of distinct type
$buckets = array_reduce($data, function($result, $item) {
    $type = $item["type"];
    if (!isset($result[$type])) {
        $result[$type] = [];
    }
    array_push($result[$type], $item);
    return $result;
}, []);
// sort buckets by size
usort($buckets, function($a, $b) {
    return count($b) - count($a);
});
// merge buckets to single array sorted by type
// and split to chunks of size of the largest bucket
$table = array_chunk(array_merge(...$buckets), count($buckets[0]));
// compute final array by merging each column
$result = [];
foreach (array_keys($table[0]) as $i) {
    $result = array_merge($result, array_column($table, $i));
}

Try it online

答案 8 :(得分:0)

我不确定我的脚本使用方法是否正确,请检查一次

 /**
 * Create login session
 * */
public void createLoginSession(String username, String accesstoken,String tokentype, String masterid,String name, Integer access){
    // Storing login value as TRUE


    editor.putBoolean(IS_LOGIN, true);
    editor.putString(KEY_USERNAME, username);
    // Storing name in pref
    editor.putString(KEY_access_token, accesstoken);

    // Storing email in pref
    editor.putString(KEY_TOKEN_TYPE, tokentype);

    editor.putString(KEY_MASTER_ID, masterid);
    editor.putString(KEY_TOKEN_TYPE, tokentype);
    editor.putString(KEY_NAME, name);
    editor.putInt(KEY_Access, access);




    // commit changes
    editor.commit();


    String user_name_new=pref.getString(KEY_USERNAME, null)

    Log.d("TAG","Pass user name :"+username+" user_name_new:"+user_name_new);
}

感谢:)

答案 9 :(得分:0)

OMG 这里有很多巨大的功能。

要求ABAC ABAC ...... 完成:

function sortTypes(array $data, array $types)
{
    $result = [];
    while (!empty($data)) {
        $currentType = current($types);
        if (!next($types)) {
            reset($types);
        }
        foreach ($data as $key => $array) {
            if ($array['type'] === $currentType) {
                $result[$key] = $array;
                unset($data[$key]);
                break;
            }
        }
    }
    return $result;
}

$types = ['A', 'B', 'A', 'C']; // gets sorted by this pattern
$result = sortTypes($data, $types);

测试:

var_export($result);
// OUT: 
[
    0 => [
        'name' => 'SomeName',
        'type' => 'A',
    ],
    5 => [
        'name' => 'SomeName',
        'type' => 'B',
    ],
    1 => [
        'name' => 'SomeName',
        'type' => 'A',
    ],
    8 => [
        'name' => 'SomeName',
        'type' => 'C',
    ],
    2 => [
        'name' => 'SomeName',
        'type' => 'A',
    ],
    6 => [
        'name' => 'SomeName',
        'type' => 'B',
    ],
    3 => [
        'name' => 'SomeName',
        'type' => 'A',
    ],
    9 => [
        'name' => 'SomeName',
        'type' => 'C',
    ],
    4 => [
        'name' => 'SomeName',
        'type' => 'A',
    ],
    7 => [
        'name' => 'SomeName',
        'type' => 'B',
    ],
]