2015-05-30 3 views
-1

Я хотел бы преобразовать массив массивов, который функционирует как хранилище ключей.Преобразование массива пар ключей-значений: ключи для значений и значения для уникальных ключей

Каждый суб массив принимает следующий вид: ['tom',['hello','world']], где [0] индекс ('tom') является «ключом», а [1] индекс (массив) является «значение».

Я хочу, чтобы все «значения» в моем массиве были уникальными ключами нового массива, а ключи в моем массиве должны были строить новые подмассивы, содержащие все предыдущие ключи, которые содержали соответствующее значение.

Например:

var myArray = [ 
    ['tom',['hello','world']], 
    ['bob',['world','foo']], 
    ['jim',['foo','bar']] 
]; 

выше вход должен достигнуть следующий вывод:

var newArray = [ 
    ['hello',['tom']], 
    ['world',['tom','bob']], 
    ['foo',['bob','jim']], 
    ['bar',['jim']], 
]; 

Как я могу это сделать?

ответ

2

Прежде чем перейти к «решению», я хотел бы обратить внимание на тот факт, что это очень плохой способ хранения и описания данных. Если вы ищете нечто похожее на ассоциативные массивы от PHP, вы должны научиться work with objects.

Объекты в JS - это просто набор пар уникальных ключей (свойств) -> значений.

В качестве объектов, ваши наборы данных будут выглядеть следующим образом:

var before = { 
    tom: ['hello','world'], 
    dick: ['world','foo'], 
    harry: ['foo','bar'] 
}; 

var after = { 
    bar: ["harry"], 
    foo: ["dick", "harry"], 
    hello: ["tom"], 
    world: ["tom", "dick"] 
}; 

Вот реализация с использованием объектов. Также наивно, но гораздо проще.

DEMO

var before = { 
 
    tom: ['hello','world'], 
 
    dick: ['world','foo'], 
 
    harry: ['foo','bar'] 
 
}; 
 

 
var after = { 
 
    bar: ["harry"], 
 
    foo: ["dick", "harry"], 
 
    hello: ["tom"], 
 
    world: ["tom", "dick"] 
 
}; 
 

 

 
function resObj(obj) { 
 
    var o = {}; 
 
    
 
    for (var k in obj) { 
 
    for (var i = 0; i < obj[k].length; i++) { 
 
     o[obj[k][i]] = o[obj[k][i]] || []; 
 
     o[obj[k][i]].push(k); 
 
    } 
 
    } 
 
    
 
    return o; 
 
} 
 

 
console.log('Expected:', after); 
 
console.log('Actual:', resObj(before));


Ниже приведен пример того, как делать то, что вы хотите с массивами. Это наивно, медленно, и я уверен, что его можно было бы улучшить, но это не относится к моему ответу.

Грубая демонстрация. Обратите внимание, что мы используем объект как обмен, что делает его почти таким же, как указано выше.

DEMO

var inp = [ 
 
    ['tom',['hello','world']], 
 
    ['dick',['world','foo']], 
 
    ['harry',['foo','bar']] 
 
]; 
 

 
var out = [ 
 
    ['hello',['tom']], 
 
    ['world',['tom','dick']], 
 
    ['foo',['dick','harry']], 
 
    ['bar',['harry']] 
 
]; 
 

 
function resArray(arr) { 
 
    var q = {}, 
 
     o = []; 
 
    
 
    for(var i = 0; i < arr.length; i++) { 
 
    for (var j = 0; j < arr[i][1].length; j++) { 
 
     q[arr[i][1][j]] = q[arr[i][1][j]] || []; 
 
     q[arr[i][1][j]].push(arr[i][0]); 
 
    } 
 
    } 
 
    
 
    for (var m in q) { 
 
    o.push([m, q[m]]); 
 
    } 
 
    
 
    return o; 
 
} 
 

 
console.log('Expected:', out); 
 
console.log('Actual:', resArray(inp));

2

Как я уже сказал в своем комментарии, вы, по сути просят вычислить инверсию многие к одному отношения. Вы можете рассматривать отношение как отображение из пары объектов. Логично, что вы сопоставляете «tom» с «hello» и «world». Обратное отношение отобразит «hello» и «world» в «tom».

Когда вы думаете о «отношениях», вы должны думать об ассоциативных контейнерах вместо массивов. Использование массивов делает ваши алгоритмы гораздо более неэффективными, если ваши ключи не являются плотно упакованными целыми числами.

Это дает правильный вывод:

var myRelation = [ 
 
    ['tom', ['hello', 'world']], 
 
    ['dick', ['world', 'foo']], 
 
    ['harry', ['foo', 'bar']] 
 
]; 
 

 
function inverse(relation) { 
 
    // This first half does the hard work of computing the inverse. 
 
    var intermediate = {}; 
 
    relation.forEach(function(outerEntry) { 
 
    outerEntry[1].forEach(function(innerEntry) { 
 
     if (!intermediate[innerEntry]) { 
 
     intermediate[innerEntry] = {}; 
 
     } 
 
     intermediate[innerEntry][outerEntry[0]] = true; 
 
    }); 
 
    }); 
 
    // This second half turns the intermediate assocative container 
 
    // back into an array of nested arrays. 
 
    var output = []; 
 
    Object.keys(intermediate).forEach(function(outerEntry) { 
 
    output.push([outerEntry, []]); 
 
    Object.keys(intermediate[outerEntry]).forEach(function(innerEntry) { 
 
     output[output.length - 1][1].push(innerEntry); 
 
    }); 
 
    }); 
 
    return output; 
 
} 
 

 
console.log(inverse(myRelation));

Если вы уронили необходимость воспроизвести оригинальный формат вывода в массивах, проблема становится немного проще.

1

Здесь представлена ​​чистая реализация вашей функции преобразования.

/** 
* Converts from a one-to-many to many-to-one relationship. 
* @param {[array]} pairs [array representing a one-to-many relationship.] 
* @return {[array]}  [array representing the same data, in a many-to-one relationship] 
*/ 
var oneToManyFlip = function(pairs){ 
    //Recall that current[0] is our 'key'. 
    //Also recall that 'prev' is the empty object we passed in. 
    var result = pairs.reduce(function(storage, current, index, array){ 
    current[1].forEach(function(element, index){ 
     if(storage[element] === undefined){ storage[element] = [];} 
     storage[element].push(current[0]); 
    }); 
    return storage; 
    }, {}); 
    return Object.keys(result).map(function(element){ 
     return [element, result[element]]; 
    }); 
} 

Надеюсь, это немного чище и легко соображает. Карта & Уменьшение может быть немного сложным, чтобы сначала обвести голову.

Подробнее о функциональных JS here. Это полезно для таких вычислений на итерациях.

Смежные вопросы