2010-10-15 43 views
22

Sử dụng Underscore.js, tôi có thể viết những điều sau đây trả về 42:Underscore.js: làm thế nào để tùy chỉnh chức năng chuỗi

_([42, 43]).chain() 
    .first() 
    .value() 

Tôi đã chức năng tùy chỉnh, chứ không phải một phần của Underscore.js gọi double():

function double(value) { return value * 2; }; 

Tôi muốn có thể gọi hàm này trong một chuỗi Gạch dưới, như thể nó là một phần của Gạch dưới. Tôi muốn viết những điều sau đây, mà tôi muốn trả lại 84:

_([42, 43]).chain() 
    .first() 
    .double() 
    .value() 

này không thể làm việc kể từ gạch không định nghĩa double(). Tôi có thể sử dụng tap() như trong:

_([42, 43]).chain() 
    .first() 
    .tap(double) 
    .value() 

này có giá trị, nhưng tap áp dụng các chức năng để đối số của nó và trả về đối số, không phải là kết quả của hàm. Vì vậy, nó trông giống như tôi sẽ cần một loại tap trả về kết quả của hàm được áp dụng cho đối số của nó. Có điều gì giống như thế này trong Underscore.js không? Tôi có thiếu một cái gì đó khủng khiếp rõ ràng?

+4

Lưu ý rằng 'double' là một [* từ dành riêng trong tương lai *] (http://bclary.com/2004/11/07/#a-7.5.3) và việc triển khai có thể ném một' SyntaxError' nếu nó được sử dụng như một Định danh. – CMS

+0

Alessandro, có bạn đã tìm ra được nêu ra rằng tôi đã cho bạn câu trả lời đúng chỉ cho câu hỏi này? Những gì tôi đặt ra không gây ô nhiễm không gian tên gạch dưới. Thư viện underscore.js dự đoán những gì bạn đã yêu cầu và xây dựng trong một tính năng cho chính xác điều đó. –

+0

Dấu gạch dưới giờ đây có cả 'tap' (chạy một hàm để sửa đổi từng mục) và 'map' (chạy một hàm trả về một mục mới). – CMircea

Trả lời

23

Vì không tìm được tap trả về giá trị lợi nhuận giá trị bằng hàm được chạy, tôi xác định một mà tôi có thể take và thêm vào _:

_.mixin({take: function(obj, interceptor) { 
    return interceptor(obj); 
}}); 

Sau đó, giả sử tôi có:

function double(value) { return value * 2; }; 

Tôi có thể viết:

_([42, 43]).chain() 
    .first()    // 42 
    .take(double)  // Applies double to 42 
    .value()    // 84 

Bạn có thể xem take làm map trên các đối tượng thay vì danh sách. Bạn muốn thử nghiệm với điều này? Xem số này example on jsFiddle.

+1

'apply' có thể là tên tốt hơn –

+4

@Gabe, tôi đã nghĩ đến việc gọi nó là' apply', nhưng tên đã được định nghĩa trên các hàm, và nó có một ngữ nghĩa khác: 'f.apply (obj, argArray)' thay vì 'obj.take (f, argArray)'. Bạn không nghĩ rằng điều này có thể là nhầm lẫn? (Trung thực yêu cầu như tôi không thuyết phục bản thân mình một trong hai cách.) – avernet

+1

Đối với những người tìm kiếm, 'tap' bây giờ là một phần cốt lõi gạch dưới. –

5

Vì vậy, bạn có một chức năng tùy chỉnh:

function double(value) { return value * 2; } 

Bạn có thể sử dụng mixin để mở rộng gạch với nó:

_.mixin({ double:double }); 

Bây giờ bạn có thể gọi chức năng của bạn từ đối tượng gạch _:

_.double(42); // 84 

và từ đối tượng được trả lại từ chain:

_([42, 43]).chain() 
    .first() 
    .double() // double made it onto the wrapped object too 
    .value(); // 84 
+0

có, đó là một cách để làm điều đó. Một cái gì đó tôi không thích về điều này là nó gây ô nhiễm '_'. I E. điều này không khả thi nếu bạn sử dụng chuỗi trên tất cả các vị trí trong mã của bạn, và do đó có rất nhiều chức năng. – avernet

+0

Tôi đề xuất một giải pháp thay thế yêu cầu tôi chỉ thêm một hàm vào '_' - xem câu trả lời cho câu hỏi của riêng tôi. Tôi sẽ thử nghiệm với điều này và xem nó hoạt động tốt như thế nào trong thực tế. – avernet

-1

map có hoạt động không?

_([42, 43]).chain() 
    .first() 
    .map(double) 
    .value() 

chỉnh sửa

từ các tài liệu, có vẻ như map sẽ chỉ làm việc nếu bạn đặt nó trước khi cuộc gọi đến first:

_([42, 43]).chain() 
    .map(double) 
    .first() 
    .value() 
+0

đúng, 'bản đồ' không cắt nó vì nó chỉ hoạt động trên danh sách. Ở đây tôi cần một loại 'bản đồ' hoạt động trên một" mục "duy nhất. – avernet

+0

@avernet bạn có thể chỉ đơn giản là tiền tố và bản đồ postfix với đầu tiên. – arcseldon

-1

Sử dụng compose là một cách khác để đối phó với tình hình, nhưng tôi nghĩ việc thêm một hàm như takeas I suggested earlier là một giải pháp tốt hơn. Tuy nhiên, đây là cách mã sẽ trông như thế với compose:

function double(value) { return value * 2; }; 

_.compose(
    double, 
    _.first, 
    _.bind(_.identity, _, [42, 43]) 
)(); 

Giá trị ban đầu cần được cung cấp thông qua một hàm trả về giá trị (ở đây được thực hiện bởi tách lạng bộ identity), và các chức năng cần phải được liệt kê trong một thứ khác ngược lại với những gì bạn có với một chuỗi, xuất hiện khá là không tự nhiên đối với tôi.

+0

Tôi đồng ý rằng đây là cách xấu hơn so với giải pháp khác mà bạn đã đăng. –

5

Được rồi, tôi đã sẵn sàng đọc mã nguồn được chú thích cuối cùng lần đầu tiên. Nhưng tôi nghĩ rằng bạn có thể làm một cái gì đó như thế này:

function double(value) { return value * 2; }; 

var obj = _([42, 43]).addToWrapper({double:double}); 

obj.chain() 
    .first() 
    .double() 
    .value(); 

Cú pháp/chi tiết có thể không đúng, nhưng điểm cốt lõi là thế này: khi bạn gọi _([42,43]), bạn đang gọi điện thoại nhấn mạnh như một hàm. Khi bạn làm như vậy, nó sẽ khởi tạo một đối tượng mới và sau đó trộn vào đối tượng đó hầu hết các hàm gạch dưới. Sau đó, nó trả về đối tượng đó cho bạn. Sau đó bạn có thể thêm các hàm của riêng bạn vào đối tượng đó và không có hàm nào trong số này gây ô nhiễm không gian tên "_".

Đó là những gì mã underscore.js trông giống như tôi. Nếu tôi sai, tôi muốn tìm hiểu và hy vọng ai đó sẽ giải thích tại sao.

EDIT: Tôi đã thực sự sử dụng underscore.js rất nhiều trong khoảng một tháng nay và tôi đã khá quen thuộc với nó. Tôi bây giờ biết nó hoạt động như tôi đã nói ở đây. Khi bạn gọi _ là hàm Constructor, bạn lấy lại "không gian tên" của riêng bạn (chỉ là một đối tượng), và bạn có thể thêm những thứ vào nó với addToWrapper() xuất hiện trong không gian tên của bạn nhưng không xuất hiện trong "toàn cục" "_" không gian tên. Vì vậy, tính năng mà OP mong muốn đã được tích hợp sẵn. (Và tôi đã thực sự ấn tượng với dấu gạch dưới, btw, nó rất được thực hiện rất tốt).

+0

Charlie, vâng, bạn có thể làm điều này. Tôi đã đề cập đến điều này trong câu hỏi của tôi: Tôi không muốn làm điều đó vì nó sẽ gây ô nhiễm không gian tên '_'. (Hãy tưởng tượng nếu bạn có một codebase lớn bằng cách sử dụng Underscore, và mỗi khi bạn muốn làm điều này, bạn cần phải thêm hàm vào Underscore.) – avernet

+2

@Alessandro Vernet - không nó sẽ không gây ô nhiễm không gian tên _. Đó là vẻ đẹp của nó. Khi bạn gọi _ ([42,43]), nó trả về một không gian tên * mới * chỉ dành cho bạn. Khi bạn thêm "gấp đôi" vào nó, không gian tên của riêng bạn sẽ có gấp đôi, nhưng _ chính nó sẽ không. –

+0

-1 _ ([42, 43]).addToWrapper trả về không xác định –

0

Không ai thực sự trả lời câu hỏi này theo yêu cầu vì vậy đây là câu trả lời của tôi cho yêu cầu mà tôi đã thử nghiệm. Vui lòng đọc ghi chú dưới đây liên quan đến đầu tiên.

function doubleValue(x) { return x * 2; } 
var rt = _.chain([42,43]) 
     .first(1) 
     .map(doubleValue) 
     .value(); 
console.log(rt); // prints out [84] 

// Lưu ý rằng điều quan trọng là nói với .first() có bao nhiêu yếu tố mà bạn muốn nếu không bạn có thể nhận được một mảng trống khác. Hãy suy nghĩ của đầu tiên như tôi muốn số lượng đầu tiên của các mặt hàng, vì vậy.đầu tiên (2) lợi nhuận [84], [86] vv

+0

Đây là v.close, nhưng thiếu một cuộc gọi bổ sung để đầu tiên() để unwrap kết quả từ mảng của nó. – arcseldon

+0

Bạn rõ ràng đã không đọc mã, mảng của nó không phải là một giá trị duy nhất được trả về, nếu bạn chỉ xác định. Đầu tiên (1) được cho một giá trị. Tôi nghĩ bạn cần phải đọc những gì mọi người viết trước khi thêm nhận xét ngớ ngẩn. –

+0

Xin vui lòng, không cần cảm xúc :). OP đã yêu cầu một giá trị, không phải là mảng đơn hoặc mảng nhiều giá trị. Có, _.first chỉ là một bí danh dùng các ngôn ngữ khác như Haskell hoặc JS libs như Ramda. Chắc chắn, nếu câu hỏi được mở rộng để có N giá trị này sẽ áp dụng. Đầu tiên (2) trả về [84, 86] (sau khi ánh xạ) và không phải [84] [86] như bạn đã chỉ ra trong Ghi chú. Đề nghị Ramda - giải pháp đơn giản trở thành R.pipe (R.head, R..multiply (2)) ([42, 43]); Hoặc nếu muốn làm những gì bạn đã minh họa: R.pipe (R.take (2), R.map (R.multiply (2))) ([42, 43]) Chúc may mắn – arcseldon

0

Nhiều cách để dễ dàng đạt được điều này, đây là một trong những giải pháp như:

_.chain([42,43]) 
    .first(1) 
    .map(double) 
    .first() 
    .value(); 
    // 84 

Tuy nhiên, tôi sẽ khuyên bạn sử dụng Ramda sau đó với auto-ri và ống/soạn các loại nhiệm vụ là tầm thường:

R.pipe(R.head, R.multiply(2))([42, 43]); // 84 

equivalent to: 

R.compose(R.multiply(2), R.head)([42, 43]); // 84 

Nếu bạn muốn mở rộng các giải pháp để tận nói 2 mục đầu tiên, thay vì một giá trị duy nhất, theo yêu cầu của OP, sau đó:

R.pipe(R.take(2), R.map(R.multiply(2)))([42, 43]) // [84, 86] 

Tuy nhiên, trong Ramda R.compose được ưu tiên. Dù bằng cách nào, đối với các nhiệm vụ tầm thường như thế này, nó có xu hướng thuận tiện và dễ sử dụng hơn.

0

Hình như lodash đã thực hiện chính xác những gì bạn đang tìm kiếm:

_.thru(value, interceptor) 

từ các tài liệu:

Phương pháp này cũng giống như _.tap ngoại trừ việc nó sẽ trả về kết quả của chặn

https://lodash.com/docs#thru

Các vấn đề liên quan