1
votes

I have a crossfilter built from a list of posts made by a user, and I want to use the data to drive a word cloud-style visualisation.

My records are an array of objects like this:

[
    {
      "date": Thu May 24 2018 11:29:27 GMT+0100 (GMT Daylight Time),
      "text": "Lorem ipsum dolor sit amet"
    },
    ...etc
]

I've set up some dimensions on the date field to filter my records, and I'd like the contents of the word cloud to update to reflect the filtered data. I'm not sure how to set up a group that is a flat array of every word in the filtered post.

Setting up a dimension like this

var textDimension = ndx.dimension(p => p.split(' '))

will give

[
    ['Lorem', 'ipsum', 'dolor',  'sit', 'amet'],
    ...etc
]

which is fine but there doesn't seem to be a way to do textDimension.group(/* do something...*/).reduceSum(r => 1) that treats the dimension as

[
    'Lorem',
    'ipsum',
    'dolor',
    'sit',
    'amet'
]

which after the reduceSum would produce something like

[
    {"key": "Lorem", "value": 1},
    {"key": "ipsum", "value": 1},
    {"key": "dolor", "value": 1},
    {"key": "sit", "value": 1},
    {"key": "amet", "value": 1},
]

instead of

[
    {"key": ['Lorem', 'ipsum', 'dolor',  'sit', 'amet'], "value": 1},
]

which is how it currently does with do logic in group(). I could do what I want with

var filteredPosts = ndx.allFiltered()
var wordsByPost = filteredPosts.map(p => p.split(' ')
var allWords = [].concat.apply([], wordsByPost)

and then count up the instances of each word in allWords (potentially by setting up a new crossfilter) but it feels like the wrong way to do it.

Is there a way to do this using the crossfilter I've already got set up or do I admit defeat and use the basic JS solution?

2
What is the expected output?Hassan Imam
I've updated the questionMourndark
@noa-dev thanks but as I'm only flattening one level deep, [].concat.apply([], wordsByPost) is fine. My package.json is full enough!Mourndark
I actually linked it so you can check the src of it and copy the function if you need to flatten n-depth :)noa-dev

2 Answers

2
votes

Once you have all the words in an array, you can flatten your array, then using array#reduce you can group the array based on the unique words in an object accumulator. Then get all values from the object using Object.values().

const data = [ { "date": 'Thu May 24 2018 11:29:27 GMT+0100 (GMT Daylight Time)', "text": "Lorem ipsum dolor sit amet" }, { "date": 'Thu May 24 2018 11:29:27 GMT+0100 (GMT Daylight Time)', "text": "Lorem ipsum2 dolor2 sit amet" } ],
    result = Object.values([].concat(...data.map(({text}) => text.split(' ')))
               .reduce((r,w) => {
                 r[w] = r[w] || {key: w, value : 0};
                 r[w].value += 1;
                 return r;
               },{}));
console.log(result);
1
votes

If you are using Crossfilter 1.4+, then you just need to tell it that the dimension is an array dimension like so:

var textDimension = ndx.dimension(p => p.split(' '), true)

Documentation: https://github.com/crossfilter/crossfilter/wiki/API-Reference#dimension_with_arrays