合并具有相似值的数组,保持值的顺序

时间:2014-03-04 09:29:32

标签: javascript algorithm merge

这是我今天遇到的一项有趣的任务,我想不出任何简单的方法来达到预期的效果。

假设我们有一个包含以下字段(列)的数据库: A,B,C,D,E,F,G 但我们不知道名称和计数字段。

我们会按以下格式从此数据库中收到一组记录: {A:value1,B:value2,...}

如果没有为当前记录设置值,则该键也将丢失。这意味着我可以将 {A:value} {C:value1,D:value2} 作为有效记录。键的顺序将始终保持不变。这意味着 {D:value,C:value} 不是有效记录。

我正在尝试根据返回的记录恢复字段名称并保持密钥的顺序。


例如,我可以使用以下键接收记录:

  • A,C,d,E,F
  • d,F,G
  • A,B,F

从上面的例子中我应该能够恢复原来的序列 A,B,C,D,E,F,G

  • 第一条记录为我们提供了 A,C,D,E,F
  • 第二个告诉我们G在F之后,所以现在我们有 A,C,D,E,F,G
  • 第三条记录告诉我们B在A之后,所以现在我们有 A,B,C,D,E,F,G

如果订单无法确定,我们可以使用字母顺序。例如:

  • A,B
  • A,C

在上面的示例中,我们无法确定原始订单是 A,B,C 还是 A,C,B

任何想法如何实现这一点在一般情况下工作?

我将使用 JavaScript 实现此算法,但 PHP C ++ Java 也是受欢迎的。< / p>


编辑:不要将对象视为标准JSON对象。在真实环境中,结构要复杂得多,语言不是纯粹的 JavaScript ,而是 ECMAScript 的修改版本。如果它更容易理解 - 只考虑键作为一组值 ['A','B','C',...] 并尝试合并它们,保持顺序。


编辑2:经过一段时间的努力并阅读了一些想法后,我提出了以下解决方案:

  • 创建一个包含所有关系的对象 - 从每个数据库记录开始的列。
  • 创建每个a-&gt; b,b-&gt; c =&gt;之间的关系。 a-&gt; c(受Floyd-Warshall启发,如果存在,则每个距离被视为1)。
  • 创建一个排序函数(比较器),检查是否可以比较两个元素。如果不是 - 将使用字母顺序。
  • 仅获取唯一的列名称并使用比较器函数对它们进行排序。

您可以在下面找到附带的源代码:

var allComparators = {};
var knownObjects = ['A,C,D,E,F','D,F,G','A,B,F'];
var allFields = knownObjects.join(',').split(',');

for (var i in knownObjects) {
    var arr = knownObjects[i].split(',');
    for (var i = 0; i < arr.length; i++) {
        for (var j = i + 1; j < arr.length; j++) {
            allComparators[arr[i]+'_'+arr[j]] = 1;
        }
    }
}

allFields = allFields.filter(function(value, index, self) { 
    return self.indexOf(value) === index;
});

for (var i in allFields) {
    for (var j in allFields) {
        for (var k in allFields) {
            if (allComparators[allFields[i]+'_'+allFields[j]] && allComparators[allFields[j]+'_'+allFields[k]]) {
                allComparators[allFields[i]+'_'+allFields[k]] = 1;
            }
        }
    }
}

allFields.sort(function(a, b) {
    if (typeof allComparators[a + '_' + b] != 'undefined') {
        return -1;
    }
    if (typeof allComparators[b + '_' + a] != 'undefined') {
        return 1;
    }
    return a > b;
});

console.log(allFields);

3 个答案:

答案 0 :(得分:1)

我以非常直接且易于理解的方式为您提供算法但代码!如果需要,请尝试自己并寻求帮助。 我用两种方式表达自己

技术术语:

  1. 生成优先图(即有向图)
  2. 拓扑排序

  3. 更多细节:

    图形:Map(String,ArrayList&lt; String&gt;)= [Map(key,value)]
    地图中的每个键对应一个元素(A,B,C,......)
    每个值包含应放在键后面的元素,例如A,它是{B,C,D,...}
    如何填写图表:

    for each row:
     for each element inside the row:
      if the element is already as a key in the map
          just add its immediate next item to the list*
      else
          add the element to the map and set the value to immediate next element of it**
    

    *如果元素是行中的最后一个元素,则不向地图添加任何内容 **如果元素是行中的最后一个,则使用{},一个空列表,作为值

    拓扑排序:

    List sortedList;
    for each key in the map:
      if value.size() == 0 {
        remove key from the map
        add it the key to the sortedList
        for each key' in the map:
          if value'.contains(key)
            value'.remove(key) (and update the map)
      }
    invert the sortedList
    

    测试用例:

    the map for your first input will be:
    { A:{C,B} , C:{D} , D:{E,F} , E:{F} , F:{G} , G:{} , B:{F} }
    
    Sort : 
    1 - G -> sortedList, map = { A:{C,B} , C:{D} , D:{E,F} , E:{F} , F:{} , B:{F} }
    2 - F -> sortedList, map = { A:{C,B} , C:{D} , D:{E} , E:{} , B:{} }
    3 - E -> sortedList, map = { A:{C,B} , C:{D} , D:{} }
    4 - D -> sortedList, map = { A:{C,B} , C:{} }
    5 - C -> sortedList, map = { A:{B} , B:{} }
    6 - B -> sortedList, map = { A:{} }
    6 - A -> sortedList, map = { }
    sortedList = {G,F,E,D,C,B,A}
    Invert - > {A,B,C,D,E,F,G} 
    

答案 1 :(得分:1)

你认为这样的事情会起作用吗?

var oMergedList = [];

function indexOfColumn(sColumnName)
{
    for(var i = 0 ; i < oMergedList.length;i++)
        if(oMergedList[i]==sColumnName)
            return i;
    return -1;
}
function getOrdinalIndex(sColumnName)
{
    var i = 0;
    for( ; i < oMergedList.length;i++)
        if(oMergedList[i]>sColumnName)
            break;
    return i;
}

function merge(oPartial)
{
    var nPreviousColumnPosition = -1;
    for(var i = 0 ; i < oPartial.length;i++)
    {
        var sColumnName =  oPartial[i] ;
        var nColumnPosition = indexOfColumn(sColumnName);
        if(nColumnPosition>=0)//already contained
        {
            if(nPreviousColumnPosition>=0 && nColumnPosition!=(nPreviousColumnPosition+1))//but inserted on wrong place
            {
                oMergedList.splice(nColumnPosition, 1);
                nColumnPosition = nPreviousColumnPosition
                 oMergedList.splice(nColumnPosition, 0, sColumnName);
            }
            nPreviousColumnPosition = nColumnPosition;
        }
        else //new
        {
            if(nPreviousColumnPosition<0)//no reference column
            {
                nPreviousColumnPosition = getOrdinalIndex(sColumnName);
            }
            else// insert after previous column
                nPreviousColumnPosition++;
            oMergedList.splice(nPreviousColumnPosition, 0, sColumnName);
        }

    }
}
/* latest sample
merge(['A','C','E','G']);
merge(['A','D']);
merge(['C','D']);
*/
/* default sample
merge(['A','C','D','E','F']);
merge(['D','F','G']);
merge(['A','B','F']);
*/
/* fix order
merge(['A','B']);
merge(['A','C']);
merge(['A','B','C']);
*/
/* insert alphabetically
merge(['B']);
merge(['A']);
merge(['C']);
*/
document.body.innerHTML = oMergedList.join(',');

唯一的“未定义”部分是在没有先前列的情况下插入的位置(我把它放在第一位) 第二个在案例A,B .. A,C中,第一次看到时会插入列

表示A,B..A,C表示A,C,B ..表示A,C..A,B表示A,B,C


编辑以使用当前数组位置进行修复 之前的添加所以如果你添加[A,C] [A,B]你会得到[A,C,B],但如果你再通过[A,B,C] 数组将被修复以反映新订单

当新列出现且没有参考列按字母顺序添加时


修正了列修正标准..现在应该给你正确的结果..

答案 2 :(得分:0)

正如JSON.org所描述的那样,没有Json订购的密钥:

  

对象是一组无序的名称/值对。

话虽如此,合并对象变得非常容易,因为您不需要订单。

for (var attrname in obj2) { obj1[attrname] = obj2[attrname]; }

来源:How can I merge properties of two JavaScript objects dynamically?