Mereproduksi fungsionalitas map/emit MongoDB di javascript/node.js (tanpa MongoDB)

Saya menyukai fungsionalitas yang disediakan MongoDB untuk melakukan tugas peta/pengurangan, khususnya emit() dalam fungsi mapper. Bagaimana saya bisa mereproduksi perilaku peta yang ditunjukkan di bawah ini dalam javascript/node.js tanpa MongoDB?

Contoh (dari MongoDB Map-Reduce Docs):

[{ cust_id: "A123", amount: 500 }, { cust_id: "A123", amount: 250 }, { cust_id: "B212", amount: 200 }] 

Dipetakan ke -

[{ "A123": [500, 200] }, { "B212": 200 }]

Pustaka yang membuatnya sesederhana satu baris emit() Mongo akan bagus tetapi fungsi asli juga bisa melakukan pekerjaan itu.


person Fuzzifized    schedule 17.12.2014    source sumber
comment
Sudahkah Anda memeriksa Underscore.JS?   -  person Chris Franklin    schedule 18.12.2014
comment
Sudah, tetapi saya tidak tahu cara menggunakan _.map untuk mendapatkan hasil yang sama.   -  person Fuzzifized    schedule 18.12.2014
comment
Dengan [].map, [].reduce dan [].forEach sebagai metode bawaan saat ini (dan lebih banyak lagi sebagai tautan jawaban pertama), Anda tidak benar-benar membutuhkan perpustakaan. Kompatibilitasnya lebih mudah untuk menggunakan Garis Bawah, karena ia menyediakannya ketika hilang, tetapi ketergantungan tambahan hanya merupakan hal yang baik jika diperlukan.   -  person SamMorrowDrums    schedule 18.12.2014


Jawaban (3)


Jika Anda hanya perlu memiliki sintaks emit, itu mungkin. Pindai isi fungsi dan teruskan fungsi emisi baru.

function mapReduce(docs, m, r) {
  var groups = {}
  function emit(key, value) {
    if (!groups[key]) { groups[key] = [] }
    groups[key].push(value)
  }
  var fn = m.toString()
  var body = fn.substring(fn.indexOf('{') + 1, fn.lastIndexOf('}'))
  var map = new Function('emit', body)
  docs.forEach(function (doc) {
    map.call(doc, emit)
  })
  var outs = []
  Object.keys(groups).forEach(function (key) {
    outs.push({ _id: key, value: r(key, groups[key]) })
  })
  return outs
}

Edit, lupa contoh:

var docs = // from above

Array.sum = function (values) {
  return values.reduce(function (a, b) { return a + b })
}

mapReduce(docs, 
  function () {
    emit(this.cust_id, this.amount)
  },
  function (k, values) {
    return Array.sum(values)
  }
)

// [ { _id: 'A123', value: 750 }, { _id: 'B212', value: 200 } ]
person AJcodez    schedule 17.12.2014
comment
Saya akan menambahkan JSFiddle untuk ini. Terima kasih. Ini adalah fungsi yang saya cari. - person Fuzzifized; 18.12.2014
comment
Contoh yang bagus, meskipun mengandung beberapa hal aneh: mengapa mengubah fungsi peta menjadi string dan mengacaukan isi fungsi hanya untuk menyuntikkan emit? Mengapa Array.sum? Jika Anda ingin menambahkan metode baru ke setiap array, Anda harus menggunakan Array.prototype.sum. - person fbuchinger; 24.01.2017

Array.reduce melakukan apa yang Anda butuhkan. berikut dokumentasinya: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce

Saya juga menyarankan Anda untuk menggunakan undescore.js (seperti pada komentar pertama) yang memiliki pengurangan & pengurangan Kanan. http://underscorejs.org/#reduce

person risyasin    schedule 17.12.2014

Saya setuju bahwa ada banyak cara lib yang bagus untuk melakukan ini, dan ini mudah dilakukan dengan metode Array. Inilah biola saran saya. Ini cukup sederhana, dan hanya menggunakan metode forEach Array. Saya telah melakukannya dalam satu putaran, tetapi ada banyak cara lainnya.

Saya belum melakukan pengurangan pada akhirnya, karena Anda tidak memintanya, tapi saya harap ini membantu.

function emit (key, value, data) {
    var res = {}; out = [];
    data.forEach(function (item) {
        var k = item[key];
        var v = item[value];
        if (k !== undefined && v !== undefined) {
            if (res[k] !== undefined) {
                out[res[k]][k].push(v);
            } else {
                var obj = {};
                res[k] = out.length;
                obj[k] = [v];
                out.push(obj);
            } 
        }
    });
    return out;
}

var data = [{name: 'Steve', amount: 50},{name: 'Steve', amount: 400}, {name: 'Jim', amount: 400}];

emit('name', 'amount', data)) // returns [{"Steve":[50,400]},{"Jim":[400]}]

emit('amount', 'name', data)) // returns [{"50":["Steve"]},{"400":["Steve","Jim"]}]

Saya telah menggunakan objek untuk menyimpan indeks array untuk setiap entri unik. Ada banyak versi mengenai hal ini. Mungkin banyak yang lebih baik dari saya, tapi saya pikir saya akan memberi Anda versi vanilla JS.

person SamMorrowDrums    schedule 17.12.2014