Подтвердить что ты не робот

Запрос на перекрестный фильтр

Можно ли фильтровать набор данных перекрестного фильтра, который имеет массив в качестве значения?

Например, скажем, у меня есть следующий набор данных:

var data = [
  {
    bookname: "the joy of clojure",
    authors: ["Michael Fogus", "Chris Houser"],
    tags: ["clojure", "lisp"]
  },
  {
    bookname: "Eloquent Ruby",
    authors: ["Russ Olsen"],
    tags: ["ruby"]
  },
  {
    bookname: "Design Patterns in Ruby",
    authors: ["Russ Olsen"],
    tags: ["design patterns", "ruby"]
  }
];

Есть ли простой способ доступа к книгам, которые помечены определенным тегом? А также книги, которые имеют конкретного автора? Насколько я понимаю, как использовать crossfilter до сих пор, я сделал что-то вроде этого:

var filtered_data = crossfilter(data);
var tags = filtered_data.dimension(function(d) {return d.tags});
var tag = tags.group();

И затем, когда я получаю доступ к группировке (например):

tag.all()

Я получаю это:

[{key: ["clojure", "lisp"], value: 1}, 
 {key: ["design patterns", "ruby"], value: 1}, 
 {key: ["ruby"], value: 1}]

Когда я предпочел бы это:

[{key: "ruby", value: 2}, 
 {key: "clojure", value: 1}, 
 {key: "lisp", value: 1},
 {key: "design patterns", value: 1}]
4b9b3361

Ответ 1

Я добавил комментарии к приведенному ниже коду. Большое изображение: используйте функцию уменьшения.

var data = ...
var filtered_data = crossfilter(data);
var tags = filtered_data.dimension(function(d) {return d.tags});

tags.groupAll().reduce(reduceAdd, reduceRemove, reduceInitial).value()

Обратите внимание, как я использовал groupAll() вместо group() b/c, мы хотим, чтобы наши функции сокращения (определенные ниже) работали в одной группе, а не 3 группы.

Теперь функции сокращения должны выглядеть так:

/*
 v is the row in the dataset

 p is {} for the first execution (passed from reduceInitial). 
 For every subsequent execution it is the value returned from reduceAdd of the prev row
*/
function reduceAdd(p, v) {
  v.tags.forEach (function(val, idx) {
     p[val] = (p[val] || 0) + 1; //increment counts
  });
  return p;
}

function reduceRemove(p, v) {
   //omitted. not useful for demonstration
}

function reduceInitial() {
  /* this is how our reduce function is seeded. similar to how inject or fold 
   works in functional languages. this map will contain the final counts 
   by the time we are done reducing our entire data set.*/
  return {};  
}

Ответ 2

Я никогда не использовал "crossfilter" (я предполагаю, что это JS-библиотека). Вот некоторые чистые JS-методы.

Это...

data.filter(function(d) {
  return d.authors.indexOf("Michael Fogus") !== -1;
})

возвращает это:

[{bookname:"the joy of clojure", authors:["Michael Fogus", "Chris Houser"], tags:["clojure", "lisp"]}]

Это...

var res = {};
data.forEach(function(d) {
  d.tags.forEach(function(tag) {
    res.hasOwnProperty(tag) ? res[tag]++ : res[tag] = 1
  });
})

возвращает это:

({clojure:1, lisp:1, ruby:2, 'design patterns':1})

Для любого из них вы можете применить d3.entries, чтобы получить формат {key:"ruby", value: 2}.