การสร้างฟังก์ชันแผนที่/การปล่อยของ MongoDB ใน javascript/node.js (โดยไม่มี MongoDB)

ฉันชอบฟังก์ชันที่ MongoDB มีให้สำหรับการทำงาน map/reduce โดยเฉพาะ emit() ในฟังก์ชัน mapper ฉันจะสร้างพฤติกรรมแผนที่ที่แสดงด้านล่างใน javascript/node.js โดยไม่มี MongoDB ได้อย่างไร

ตัวอย่าง (จาก MongoDB Map-Reduce Docs):

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

แมปไปที่ -

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

ไลบรารี่ที่ทำให้มันง่ายเหมือนกับการใช้บรรทัดเดียวของ Mongo emit() คงจะดี แต่ฟังก์ชันเนทิฟก็ใช้ได้เช่นกัน


person Fuzzifized    schedule 17.12.2014    source แหล่งที่มา
comment
คุณได้ดู Underscore.JS แล้วหรือยัง   -  person Chris Franklin    schedule 18.12.2014
comment
ฉันมี แต่ฉันไม่สามารถทราบวิธีใช้ _.map เพื่อให้ได้ผลลัพธ์เดียวกัน   -  person Fuzzifized    schedule 18.12.2014
comment
ด้วย [].map, [].reduce และ [].forEach เป็นวิธีการในตัวในปัจจุบัน (และอีกมากเป็นคำตอบแรกลิงก์ไป) คุณไม่จำเป็นต้องมีไลบรารีจริงๆ การใช้ขีดล่างจะสะดวกสำหรับความเข้ากันได้ เนื่องจากจะมีให้เมื่อขาดหายไป แต่การขึ้นต่อกันเพิ่มเติมจะเป็นสิ่งที่ดีหากจำเป็นเท่านั้น   -  person SamMorrowDrums    schedule 18.12.2014


คำตอบ (3)


หากคุณต้องการเพียงแค่มีไวยากรณ์ emit ก็เป็นไปได้ สแกนส่วนเนื้อหาของฟังก์ชันและส่งผ่านฟังก์ชันปล่อยใหม่

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
}

แก้ไข ลืมตัวอย่าง:

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
ฉันจะเพิ่ม JSFiddle สำหรับสิ่งนี้ ขอบคุณ นี่คือฟังก์ชันที่ฉันกำลังมองหา - person Fuzzifized; 18.12.2014
comment
ตัวอย่างที่ดี ถึงแม้ว่ามันจะมีสิ่งแปลก ๆ อยู่บ้าง: เหตุใดจึงแปลงฟังก์ชันแผนที่เป็นสตริงและยุ่งกับตัวฟังก์ชันเพียงเพื่อฉีดการปล่อย ทำไมต้อง Array.sum? หากคุณต้องการเพิ่มเมธอดใหม่ให้กับแต่ละอาร์เรย์ คุณต้องใช้ Array.prototype.sum - person fbuchinger; 24.01.2017

Array.reduce ทำสิ่งที่คุณต้องการ นี่คือเอกสารประกอบ: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce

ฉันขอแนะนำให้คุณใช้ undescore.js (เหมือนในความคิดเห็นแรก) ซึ่งมีการย่อ & ลดขวา http://underscorejs.org/#reduce

person risyasin    schedule 17.12.2014

ฉันยอมรับว่ามีวิธี lib ที่ยอดเยี่ยมมากมายในการทำเช่นนี้ และวิธี Array ก็ทำได้ง่ายมาก นี่คือซอสำหรับข้อเสนอแนะของฉัน มันค่อนข้างง่ายและใช้วิธี forEach Array ฉันทำไปแล้วในวงเดียว แต่มีวิธีอื่นอีกมากมาย

ฉันไม่ได้ทำการลดในตอนท้าย เนื่องจากคุณไม่ได้ขอสิ่งนั้น แต่ฉันหวังว่านี่จะช่วยได้

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"]}]

ฉันใช้วัตถุเพื่อจัดเก็บดัชนีอาร์เรย์สำหรับแต่ละรายการที่ไม่ซ้ำกัน มีหลายเวอร์ชันของเรื่องนี้ อาจดีกว่าของฉันมาก แต่คิดว่าฉันจะให้เวอร์ชัน vanilla JS แก่คุณ

person SamMorrowDrums    schedule 17.12.2014