2014-09-02 30 views
5
val data = List("foo", "bar", "bash") 
val selection = List(0, 2) 
val selectedData = data.filter(datum => selection.contains(datum.MYINDEX)) 
//             INVALID CODE HERE^
// selectedData: List("foo", "bash") 

Giả sử tôi muốn lọc List được cung cấp danh sách các chỉ mục đã chọn. Nếu, trong phương thức filter, tôi có thể tham chiếu chỉ mục của một mục danh sách sau đó tôi có thể giải quyết vấn đề này như trên, nhưng datum.MYINDEX không hợp lệ trong trường hợp trên.Lọc danh sách theo chỉ mục mục?

Làm cách nào để thay thế?

Trả lời

6

Cách sử dụng zipWithIndex để giữ tham chiếu đến chỉ mục của mục, lọc như vậy, sau đó ánh xạ chỉ mục đó đi?

data.zipWithIndex 
    .filter{ case (datum, index) => selection.contains(index) } 
    .map(_._1) 
+0

một điểm cuối cùng (sau khi đọc liên kết bạn đã cung cấp) là trích dẫn trích dẫn của bạn - đối số ngoặc đơn là đối số tuổi Ai Cập, trong ngôn ngữ như Java là * tùy chọn * (trong Scala thì không). Bây giờ tôi không nhận thức được bất kỳ ngôn ngữ nào mà nó là OK để đặt một tuyên bố/biểu hiện trên * cùng một dòng * như khung mở, đó là khách quan sai, không sở thích cá nhân. – samthebest

+0

Điều duy nhất tôi thừa nhận là hướng dẫn là một chút tiềm ẩn về điều này, và không gian trước khi vấn đề brace, và chỉ rõ ràng rằng đóng niềng răng nên được trên dòng riêng của mình ".. đóng ngoặc là trên dòng riêng của mình ngay lập tức sau dòng cuối cùng của hàm "#:. Vì vậy, tôi sẽ chỉnh sửa một lần cuối cùng, theo phần rõ ràng, khách quan đúng, một phần của hướng dẫn về phong cách và để lại tất cả các lỗi tiềm ẩn cho bạn. – samthebest

+0

@samthebest, có rất nhiều công cụ và cài đặt định dạng khác nhau. Tại sao bạn nhấn mạnh vào việc sử dụng một cụ thể? – Basilevs

0

giải pháp đầu tiên mà đến tâm trí của tôi là tạo ra một danh sách các cặp (element, index), lọc mọi phần tử bằng cách kiểm tra nếu lựa chọn chứa chỉ số, sau đó bản đồ danh sách kết quả để giữ chỉ elementd thô (chỉ mục bỏ qua). Mã là tự giải thích:

data.zipWithIndex.filter(pair => selection.contains(pair._2)).map(_._1) 

hoặc dễ đọc hơn:

val elemsWithIndices = data.zipWithIndex 
val filteredPairs = elemsWithIndices.filter(pair => selection.contains(pair._2)) 
val selectedElements = filteredPairs.map(_._1) 
+0

Có lẽ thứ 2 dễ đọc hơn đối với một người có trải nghiệm Scala 2 ngày, nhưng cách đầu tiên là những nhà phát triển Scala thành ngữ nhất sẽ thích nó vì sự sang trọng của nó. – samthebest

1

Đó là gọn gàng để làm điều đó theo cách khác về (mặc dù có khả năng chậm với danh sách như đánh chỉ số là chậm (O (n)) Vectors. sẽ tốt hơn. Mặt khác, các contains của giải pháp khác cho mỗi mục trong data là không chính xác nhanh)

val data = List("foo", "bar", "bash") 
     //> data : List[String] = List(foo, bar, bash) 
val selection = List(0, 2) 
     //> selection : List[Int] = List(0, 2) 
selection.map(index=>data(index)) 
     //> res0: List[String] = List(foo, bash) 
+0

Điều này sẽ ném một ngoại lệ nếu bạn có chỉ mục lớn hơn kích thước của danh sách 'dữ liệu'. –

+0

Có. OP có thể cho biết đó có phải là vấn đề hay không và hành vi sẽ là gì đối với chỉ mục không hợp lệ. –

+0

Tuy nhiên, ngoại lệ ném thường không phải là hành vi tốt. –

0

vì bạn có một danh sách các chỉ số alread y, cách hiệu quả nhất là phải chọn những chỉ số trực tiếp:

val data = List("foo", "bar", "bash") 
val selection = List(0, 2) 
val selectedData = selection.map(index => data(index)) 

hoặc thậm chí:

val selectedData = selection.map(data) 

hoặc nếu bạn cần để giữ gìn trật tự của các mục trong dữ liệu:

val selectedData = selection.sorted.map(data) 

CẬP NHẬT

Trong tinh thần tìm tất cả các tiện ích thuật toán điện tử, đây là phiên bản sử dụng collect:

val selectedData = data 
    .zipWithIndex 
    .collect { 
    case (item, index) if selection.contains(index) => item 
    } 
+1

Điều này sẽ ném một ngoại lệ nếu bạn có chỉ mục lớn hơn kích thước của danh sách 'dữ liệu'. –

+0

Vâng, điểm tốt. –

+0

Nhưng có thể ngoại lệ là hành vi bắt buộc. Cũng giống như thực hiện một chỉ mục đơn lẻ đơn giản của Danh sách với chỉ mục quá lớn. –

0

này Tác phẩm:

val data = List("foo", "bar", "bash") 
val selection = List(0, 2) 
val selectedData = data.filter(datum => selection.contains(data.indexOf(datum))) 
println (selectedData) 

đầu ra: Danh sách (foo, bash)

0

Sau đây là cách có lẽ là khả năng mở rộng nhất để làm điều đó về hiệu quả, và không giống như nhiều câu trả lời trên SO, thực sự theo hướng dẫn chính thức về phong cách scala chính xác.

import scala.collection.immutable.HashSet 

val selectionSet = new HashSet() ++ selection 

data.zipWithIndex.collect { 
    case (datum, index) if selectionSet.contains(index) => datum 
} 

Nếu bộ sưu tập kết quả là để được thông qua để thêm map, flatMap, vv, đề nghị chuyển data thành một chuỗi lười biếng. Trong thực tế có lẽ bạn nên làm điều này anyway để tránh 2-pass, một cho zipWithIndex một cho collect, nhưng tôi nghi ngờ khi một điểm chuẩn sẽ đạt được nhiều.

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