2015-07-04 3 views
14

У меня есть объект JSON в этом формате.Добавление свойств объекта в объект JSON

[ 
    { 
     "name": "schoolname", 
     "line id": "0", 
     "time": "4-5", 
     "minage": "15", 
     "maxage": "35" 
    }, 
    { 
     "name": "studentname1", 
     "line id": "1", 
     "class": "A" 
    }, 
    { 
     "name": "studentname2", 
     "line id": "2", 
     "class": "B" 
    } 
] 

То, что я хочу сделать

Из набора определенных заголовков, получить его из "line id" : "0" и установить его на другие предметы.

Для примера: headers = ["time", "minage", "maxage"]

Я получаю их от "line id" : "0" и дать его другим, как это.

[ 
    { 
     "name": "schoolname", 
     "line id": "0", 
     "time": "4-5", 
     "minage": "15", 
     "maxage": "35" 
    }, 
    { 
     "name": "studentname1", 
     "line id": "1", 
     "class": "A", 
     "time": "4-5", 
     "minage": "15", 
     "maxage": "35" 
    }, 
    { 
     "name": "studentname2", 
     "line id": "2", 
     "class": "B", 
     "time": "4-5", 
     "minage": "15", 
     "maxage": "35" 
    } 
] 

, а затем удалить элемент с "line id" : "0", как это:

[ 
    { 
     "name": "studentname1", 
     "line id": "1", 
     "class": "A", 
     "time": "4-5", 
     "minage": "15", 
     "maxage": "35" 
    }, 
    { 
     "name": "studentname2", 
     "line id": "2", 
     "class": "B", 
     "time": "4-5", 
     "minage": "15", 
     "maxage": "35" 
    } 
] 

Говорят, что первый элемент будет "line id" : "0".

Что я пробовал:

var headers = ["time", "minage", "maxage"] 
var data = 
    [ 
     { 
      "name": "schoolname", 
      "line id": "0", 
      "time": "4-5", 
      "minage": "15", 
      "maxage": "35" 
     }, 
     { 
      "name": "studentname1", 
      "line id": "1", 
      "class": "A" 
     }, 
     { 
      "name": "studentname2", 
      "line id": "2", 
      "class": "B" 
     } 
    ]; 

for(var i = 1; i < data.length; i++)//iterate over the data leaving the 1st line 
{ 
    for(var j = 0; j < headers.length; j++)//add each header to the data lines 
    { 
     data[i][headers[j]] = data[0][headers[j]]; 
    } 
} 
data.splice(0,1); 

Все работает отлично и, как предполагалось. Есть ли способ уменьшить временную сложность этого и сделать его более эффективным.

У этого теперь есть временная сложность O (n * m).

Есть ли способ добавить эти несколько объектов ко всем элементам? Поскольку пары ключевых значений, которые должны быть добавлены для всех записей, остаются неизменными.

ответ

9

Вы можете использовать Object.defineProperties как

var arr = [{ 
 
    "name": "schoolname", 
 
    "line id": "0", 
 
    "time": "4-5", 
 
    "minage": "15", 
 
    "maxage": "35" 
 
    }, { 
 
    "name": "studentname1", 
 
    "line id": "1", 
 
    "class": "A" 
 
    }, { 
 
    "name": "studentname2", 
 
    "line id": "2", 
 
    "class": "B" 
 
    }], 
 
    headers = ["time", "minage", "maxage"]; 
 

 

 
function addHeaders(arr, headers) { 
 
    var header = arr.splice(0, 1)[0], 
 
    propObj = headers.reduce(function(acc, el) { 
 
     acc[el] = { 
 
     value: header[el], 
 
     writable: true, 
 
     enumerable: true 
 
     }; 
 
     return acc; 
 
    }, {}); 
 

 
    for (var i = 0, len = arr.length; i < len; i++) { 
 
    Object.defineProperties(arr[i], propObj); 
 
    } 
 
    return arr; 
 
} 
 

 
document.getElementById('r').innerHTML = 'initial: ' + JSON.stringify(arr,null,2) + '<br/>'; 
 
document.getElementById('r').innerHTML += 'result: ' + JSON.stringify(addHeaders(arr, headers),null,2);
<pre id="r"></pre>

+0

Выполняет ли это исполнение оригинал? – JcT

+1

@JcT, наивный тест говорят, что оригинал быстрее – Grundy

+0

Любят ясность этого решения, очень читабельны. – JcT

7

Этот формат данных вы исправили? Вы должны подумать о том, чтобы сделать что-то более похожее

school 
    -> info (name, etc.) 
    -> [classes] 
     -> info 
     -> [student_ids] 
    -> [students] 
     -> info (id) 

Если вы не можете изменить свой формат. Вы можете сделать что-то вроде того, что хотите, с Underscore.js#default. Предполагая, что line_id=0 всегда data[0]:

var keys = ['minage','maxage','time']; 
var temp = _.pick(data.shift(),keys); 
data.forEach(function(e, i, a) { 
    a[i] = _.default(e,temp); 
}); 

Это не реально уменьшить сложность, потому что вы в основном ищете массив размера N и обновления свойств COUNT M означает, что вы будете иметь сложность O (N * М). Если вы хотите что-то менее сложное, не перемещайте и не копируйте данные. Повторно используйте его в текущей форме.

+0

Это не то, что настоящие данные. Я просто изменил «ключ» и «значения» его на случайное имя. –

3

Поскольку вы говорите, что копируете одни и те же значения из 0-го элемента, вы можете сохранить его в переменной (скажем new_data), а затем перебрать массив data и просто добавить их туда.
Это так же сложно, как итерация через data и вставка пар ключ-вал.
Что-то вроде этого -

> new_data = {} 
//Getting all the content with header keys in data into new_data 
> headers.forEach(function(v){new_data[v] = data[0][v]}) 

//Storing the new_data 
> new_data 
Object {time: "4-5", minage: "15", maxage: "35"} 

//Adding the new_data into data 
> data.forEach(function(d_val){ 
    for(k_nd in new_data){ 
     d_val[k_nd] = new_data[k_nd]; 
    } 
    }); 

//Removing the 0th array element 
> data.splice(0, 1) 

//Checking it 
> JSON.stringify(data[0]) 
"{"name":"studentname1","line id":"1","class":"A","time":"4-5","minage":"15","maxage":"35"}" 
+1

Это все еще 'O (n * m)'. В любом случае это была хорошая идея. –

3

Использование lodash библиотеки:

var headers = ["time", "minage", "maxage"]; 
 
    var data = [{ 
 
     "name": "schoolname", 
 
     "line id": "0", 
 
     "time": "4-5", 
 
     "minage": "15", 
 
     "maxage": "35" 
 
    }, { 
 
     "name": "studentname1", 
 
     "line id": "1", 
 
     "class": "A" 
 
    }, { 
 
     "name": "studentname2", 
 
     "line id": "2", 
 
     "class": "B" 
 
    }]; 
 

 
    var temp = _.pick(data[0], headers); 
 
    data.splice(0, 1); 
 
    for (var i = 0; i < data.length; i++) { 
 
     _.merge(data[i], temp); 
 
    } 
 
var result = JSON.stringify(data); 
 
    
 
$('#result').text(result);
<script src="//cdnjs.cloudflare.com/ajax/libs/lodash.js/3.10.0/lodash.min.js"></script> 
 
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> 
 

 
<div id="result"></div>

3


РЕДАКТИРОВАТЬ: Найденный значительным производительность руля, быстрее, чем все другие тестируемых растворов до сих пор: извлечение одного из контуров и нанесения с помощью: new Function(...). По существу eval -подобное приближение Object.defineProperties(...). Добавили это в тестах производительности ниже:

function addHeadersNewFunc(arr, headers) { 
    //console.time('addHeadersNewFunc'); 
    var header = arr.shift(), 
    funcBody = ['return item;'], 
    headerPropName, 
    setProps; 
    for(var h = headers.length; h--;) { 
     headerPropName = headers[h]; 
     funcBody.unshift('item["' + headerPropName + '"]="' + header[headerPropName] + '";'); //unshift since loop is reversed and we want props in same add order as other implementations, and we've already added our first line 
    } 
    setProps = new Function('item', funcBody.join('')); //warning, this is a form of 'eval()'... 

    for (var i = arr.length; i--;) 
    { 
     setProps(arr[i]); 
    } 
    //console.timeEnd('addHeadersNewFunc'); 
    return arr; 
    } 

Некоторые интересные результаты тестирования несколько различных подходов. Я только что взломал код тестирования производительности, довольный любыми рекомендованными улучшениями. Я также добавил некоторые дополнительные реализации - подход с заменой строк и ленивый getter.

В целом, похоже, что исходный цикл превосходит большинство других предложений; за исключением реализации @Chris Anderson-MSFT с использованием defaults Underscore при тестировании в Chrome, который, по-видимому, был быстрее (в IE тоже не тарифицировался). В противном случае, лазы также выполнялись хорошо. (* EDIT:, как указано выше, реализация с использованием new Function() в конечном итоге оказалась наиболее быстрой, для больших объектов/итераций, значительно).


Пример вывода из ниже фрагмент кода (Chrome 43):

Items: 2000 
Output of functions is all consistent: true 
Testing... 

addHeadersOrig x 1000: [Avg] 2.3977ms, [Min] 2.3170ms, [Max] 2.8280ms 
addHeadersDefineProp x 1000: [Avg] 6.3481ms, [Min] 6.1010ms, [Max] 15.1750ms 
addHeadersStrReplace x 1000: [Avg] 3.0551ms, [Min] 2.6630ms, [Max] 5.9910ms 
addHeadersUnderscoreDefaults x 1000: [Avg] 1.4344ms, [Min] 1.1800ms, [Max] 9.5100ms 
addHeadersLazy x 1000: [Avg] 2.4529ms, [Min] 2.3460ms, [Max] 6.0770ms 
addHeadersLazyMemo x 1000: [Avg] 2.4837ms, [Min] 2.3760ms, [Max] 3.8420ms 
addHeadersNewFunc x 1000: [Avg] 0.0959ms, [Min] 0.0430ms, [Max] 0.5070ms 

(function() { 
 

 
    "use strict"; 
 

 
    var arr = [{ 
 
     "name": "schoolname", 
 
     "line id": "0", 
 
     "time": "4-5", 
 
     "minage": "15", 
 
     "maxage": "35" 
 
    }, { 
 
     "name": "studentname1", 
 
     "line id": "1", 
 
     "class": "A" 
 
    }, { 
 
     "name": "studentname2", 
 
     "line id": "2", 
 
     "class": "B" 
 
    }], 
 
    headers = ["time", "minage", "maxage"]; 
 

 
    //add some more... 
 
    for (var i = 3, iLen = 2000; i < iLen; i++) { 
 
    arr.push({ 
 
     name: "studentname" + i, 
 
     "line id": String(i), 
 
     "class": "C" 
 
    }); 
 
    } 
 

 
    function addHeadersOrig(arr, headers) { 
 
    //console.time('addHeadersOrig'); 
 
    for (var i = 1; i < arr.length; i++) //iterate over the data leaving the 1st line 
 
    { 
 
     for (var j = 0; j < headers.length; j++) //add each header to the data lines 
 
     { 
 
     arr[i][headers[j]] = arr[0][headers[j]]; 
 
     } 
 
    } 
 
    arr.splice(0, 1); 
 
    //console.timeEnd('addHeadersOrig'); 
 
    return arr; 
 
    } 
 

 
    function addHeadersDefineProp(arr, headers) { 
 
    //console.time('addHeadersDefineProp'); 
 
    var header = arr.splice(0, 1)[0], 
 
     propObj = headers.reduce(function headerReduce(acc, el) { 
 
     acc[el] = { 
 
      value: header[el], 
 
      writable: true, 
 
      enumerable: true 
 
     }; 
 
     return acc; 
 
     }, {}); 
 

 
    for (var i = 0, len = arr.length; i < len; i++) { 
 
     Object.defineProperties(arr[i], propObj); 
 
    } 
 
    //console.timeEnd('addHeadersDefineProp'); 
 
    return arr; 
 
    } 
 

 
    function addHeadersStrReplace(arr, headers) { 
 
    //console.time('addHeadersStrReplace'); 
 
    var header = arr.shift(), 
 
     propObj = {}; 
 
    for (var i = 0; i < headers.length; i++) { 
 
     propObj[headers[i]] = header[headers[i]]; 
 
    } 
 
    //stringify the array, replace each '}' with a ',' followed by the the stringified propObj (minus its opening bracket) which brings its own closing bracket to make up for the one we replaced; then parse back to an object 
 
    arr = JSON.parse(JSON.stringify(arr).replace(/\}/g, ',' + JSON.stringify(propObj).slice(1))); 
 
    //console.timeEnd('addHeadersStrReplace'); 
 
    return arr; 
 
    } 
 

 
    //only runs using lodash, not underscore 
 
    function addHeadersLodashMerge(arr, headers) { 
 
    //console.time('addHeadersLodashMerge'); 
 
    var temp = _.pick(arr.shift(), headers); 
 
    for (var i = 0; i < arr.length; i++) { 
 
     _.merge(arr[i], temp); 
 
    } 
 
    //console.timeEnd('addHeadersLodashMerge'); 
 
    return arr; 
 
    } 
 

 
    //runs under both lodash and underscore - faster in underscore AFAICT 
 
    function addHeadersUnderscoreDefaults(arr, headers) { 
 
    //console.time('addHeadersUnderscoreDefaults'); 
 
    var temp = _.pick(arr.shift(), headers); 
 
    arr.forEach(function(e, i, a) { 
 
     a[i] = _.defaults(e, temp); 
 
    }); 
 
    //console.timeEnd('addHeadersUnderscoreDefaults'); 
 
    return arr; 
 
    } 
 
    
 
    function addHeadersNewFunc(arr, headers) { 
 
    //console.time('addHeadersNewFunc'); 
 
    var header = arr.shift(), 
 
    funcBody = ['return item;'], 
 
    headerPropName, 
 
    setProps; 
 
    for(var h = headers.length; h--;) { 
 
     headerPropName = headers[h]; 
 
     funcBody.unshift('item["' + headerPropName + '"]="' + header[headerPropName] + '";'); //unshift since loop is reversed and we want props in same add order as other implementations, and we've already added our first line 
 
    } 
 
    setProps = new Function('item', funcBody.join('')); //warning, this is a form of 'eval()'... 
 
    
 
    for (var i = arr.length; i--;) 
 
    { 
 
     setProps(arr[i]); 
 
    } 
 
    //console.timeEnd('addHeadersNewFunc'); 
 
    return arr; 
 
    } 
 

 
    function addHeadersLazy(arr, headers) { 
 
    //console.time('addHeadersLazy'); 
 
    var lazy = new Lazy(arr, headers), 
 
     result = []; 
 
    for (var i = 1; i < arr.length; i++) { 
 
     result.push(lazy.get(i)); 
 
    } 
 
    //console.timeEnd('addHeadersLazy'); 
 
    return result; 
 
    } 
 

 
    function addHeadersLazyMemo(arr, headers) { 
 
    //console.time('addHeadersLazyMemo'); 
 
    var lazy = new Lazy(arr, headers, true), 
 
     result = []; 
 
    for (var i = 1; i < arr.length; i++) { 
 
     result.push(lazy.get(i)); 
 
    } 
 
    //console.timeEnd('addHeadersLazyMemo'); 
 
    return result; 
 
    } 
 

 
    function Lazy(arr, headers, useMemo) { 
 
    var headerValSrc = arr[0], 
 
     headerLen = headers.length, 
 
     memo = []; 
 

 
    function _get(index) { 
 
     for (var j = 0; j < headerLen; j++) { 
 
     arr[index][headers[j]] = headerValSrc[headers[j]]; 
 
     } 
 
     return arr[index]; 
 
    } 
 

 
    function _getMemo(index) { 
 
     if (memo[index]) { 
 
     return memo[index]; 
 
     } 
 

 
     for (var j = 0; j < headerLen; j++) { 
 
     arr[index][headers[j]] = headerValSrc[headers[j]]; 
 
     } 
 
     return (memo[index] = arr[index]); 
 
    } 
 

 
    return { 
 
     get: (useMemo ? _getMemo : _get) 
 
    }; 
 
    } 
 

 
    function clone(data) { 
 
    return JSON.parse(JSON.stringify(data)); 
 
    } 
 

 
    function perfTest(name, testFunc) { 
 
    name = name ? name : "Test"; 
 

 
    var iterations = 1000, 
 
     argsSliced = Array.prototype.slice.call(arguments, 2), 
 
     args = [], 
 
     t0 = 0, 
 
     t1, 
 
     t2, 
 
     t3, 
 
     tmin = 1000000, 
 
     tmax = 0, 
 
     output; 
 
    setTimeout(function delayAllowingDocWrite() { 
 
     for (var i = 0; i < iterations; i++) { 
 
     args = clone(argsSliced); 
 
     t1 = performance.now(); 
 
     testFunc.apply(this, args); 
 
     t2 = performance.now(); 
 
     t3 = t2 - t1; 
 
     tmin = t3 < tmin ? t3 : tmin; 
 
     tmax = t3 > tmax ? t3 : tmax; 
 
     t0 += t3; 
 
     } 
 

 

 
     output = name + " x " + iterations + ": [Avg] " + (t0/iterations).toFixed(4) + "ms, [Min] " + tmin.toFixed(4) + "ms, [Max] " + tmax.toFixed(4) + "ms"; 
 
     console.log(output); 
 
     document.body.innerHTML += (output + "<br />"); 
 
    }, 10); 
 

 
    return testFunc.apply(this, clone(argsSliced)); //return output of function immed, once, for comparing results 
 
    } 
 

 
    document.body.innerHTML += "Items: " + arr.length + "<br />"; 
 
    console.log("Items: ", arr.length); 
 

 
    //* 
 
    var resultOrig = perfTest("addHeadersOrig", addHeadersOrig, arr, headers), 
 
    resultDefineProp = perfTest("addHeadersDefineProp", addHeadersDefineProp, arr, headers), 
 
    resultStrReplace = perfTest("addHeadersStrReplace", addHeadersStrReplace, arr, headers), 
 
    //resultLodashMerge = perfTest("addHeadersLodashMerge", addHeadersLodashMerge, arr, headers), //re-enable if using lodash.min.js 
 
    resultUnderscoreDefaults = perfTest("addHeadersUnderscoreDefaults", addHeadersUnderscoreDefaults, arr, headers), 
 
    resultLazy = perfTest("addHeadersLazy", addHeadersLazy, arr, headers), 
 
    resultLazyMemo = perfTest("addHeadersLazyMemo", addHeadersLazyMemo, arr, headers), 
 
    resultNewFunc = perfTest("addHeadersNewFunc", addHeadersNewFunc, arr, headers); 
 
    //*/ 
 

 
    var resultOrigStr = JSON.stringify(resultOrig), 
 
    outputIsConsistent = "Output of functions is all consistent: " + (
 
     resultOrigStr === JSON.stringify(resultDefineProp) && 
 
     resultOrigStr === JSON.stringify(resultStrReplace) && 
 
     //resultOrigStr === JSON.stringify(resultLodashMerge) && 
 
     resultOrigStr === JSON.stringify(resultUnderscoreDefaults) && 
 
     resultOrigStr === JSON.stringify(resultLazy) && 
 
     resultOrigStr === JSON.stringify(resultLazyMemo) && 
 
     resultOrigStr === JSON.stringify(resultNewFunc) 
 
    ); 
 
    document.body.innerHTML += outputIsConsistent + "<br /><em>Testing...</em><br /><br />"; 
 
    console.log(outputIsConsistent); 
 

 
    if (!window.performance || !window.performance.now) { 
 
    document.body.innerHTML += "Your browser does not seem to support performance.now()..."; 
 
    } 
 

 
    /* 
 
    var arr1 = clone(arr), 
 
    arr2 = clone(arr), 
 
    arr3 = clone(arr), 
 
    arr4 = clone(arr), 
 
    arr5 = clone(arr), 
 
    arr6 = clone(arr); 
 
    var resultOrig = addHeadersOrig(arr1, headers), 
 
    resultDefineProp = addHeadersDefineProp(arr2, headers), 
 
    resultStrReplace = addHeadersStrReplace(arr3, headers), 
 
    resultLodash = addHeadersLodash(arr4, headers), 
 
    resultLazy = addHeadersLazy(arr5, headers), 
 
    resultLazyMemo = addHeadersLazyMemo(arr6, headers); 
 
    console.log(resultOrig); 
 
    console.log(resultDefineProp); 
 
    console.log(resultStrReplace); 
 
    console.log(resultLodash); 
 
    console.log(resultLazy); 
 
    console.log(resultLazyMemo); 
 
    //*/ 
 

 

 
})();
body { 
 
    font-size: 0.8em; 
 
    font-family: "Arial", sans-serif; 
 
}
<!--script src="//cdnjs.cloudflare.com/ajax/libs/lodash.js/3.10.0/lodash.min.js"></script--> 
 
<script src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script> 
 
<p>Use a browser that supports performance.now().</p>


Для облегчения играть с: Plnkr

+0

Это на самом деле очень хорошее исследование. Спасибо. –

+0

Приветствия - отличный вопрос, это было очень интересно! Я хотел бы лучше понять, как функция подчеркивания '.defaults' подчёркивает так хорошо V8, но я еще не понял ее.Большинство вышеперечисленных тестов также различались по способу обработки заголовков, я, вероятно, должен был также выделить это немного; но интересным в любом случае. – JcT

+0

Нашли интересное и высокопроизводительное решение с использованием 'new Function (...)', добавили к вышеперечисленному. – JcT

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