1

I am trying to create an array of category objects from an array of materialized category paths.

var data = [
    'Business / Finance',
    'Business / Management',
    'Business / Management / Leadership',
    'Business / Team / Leadership'
];

// Expected results:
var result = [
    { name: 'Business', trail: null, path: 'Business' },
    { name: 'Finance', trail: 'Business', path: 'Business / Finance' }, 
    { name: 'Management', trail: 'Business', path: 'Business / Management' },
    { name: 'Leadership', trail: 'Business / Management', path: 'Business / Management / Leadership' }, 
    { name: 'Team', trail: 'Business', path: 'Business / Team / Leadership' },
    { name: 'Leadership', trail: 'Business / Team', path: 'Business / Team / Leadership' }
];

As you can see, Business should only be present once, since all others are only subcategories. However, Leadership should be present twice, because both are in a different structure.

When you check out the fiddle http://jsfiddle.net/9uC9Z/ you can see that Business exists 4 times.

How can I solve the problem?

I would really appreciate code comments if the resulting code is very complex.

Edit: The materialized path strings in the data array reflect category hierarchies for books. An example would be:

{
    title: 'Leadership 101',
    author: 'John Smith',
    category: 'Business / Management / Leadership'
}

That just represents one book. I now want to create one MongoDB document for every category. Above sample book would produce three category objects (Business, Management, Leadership). However, if a category (or a subcategory) object/document already exists, I don't need to create another one. resulttherefore represents the category objects I will store inside my MongoDB collection. (I will add relationships between the categories, but that isn't part of the current problem.)

John B.
  • 2,309
  • 5
  • 23
  • 22
  • Hi, Are you asking how restructure "data"? It's currently an array of strings. Are you asking how to changes the representation of values in "data"? Or are required to have an array of strings as your input data? Sorry, it's not clear to me. – lorinpa Jan 26 '14 at 18:38
  • @user2658013 Thanks for your comment. I edited my post above. Let me know if I still left something unclear. – John B. Jan 26 '14 at 19:16

1 Answers1

0

Functional approach:

function extract (path, trail) {
    if (path.length === 0) {
        return [];
    }
    var item = {
        name: path[path.length - 1],
        trail: trail.length === 0 ? null : trail.join(' / '),
        path: path.join(' / ')
    };
    var result = extract(path.slice(0, -1), path.slice(0, -2)).concat([item]);
    return result;
}

function distinct (xs) {
    function eq (a, b) {
        return JSON.stringify(a) === JSON.stringify(b);
    }

    function contains (xs, x) {
        for (var i = xs.length - 1; i >= 0; i--) {
            if (eq(xs[i], x)) {
                return true;
            }
        }
        return false;
    }

    var result = [];
    for (var i = xs.length - 1; i >= 0; i--) {
        if (!contains(result, xs[i])) {
            result.push(xs[i]);
        }
    }
    return result;
}

var result = data.
  map(function(x) { return x.split(' / ') }).
  map(function(x) { return extract(x, x.slice(0, -1)) }).
  reduce(function(a, b) { return a.concat(b)});

result = distinct(result);

You can replace distinct function with something more robust from some library. And be careful using JSON.stringify(a) === JSON.stringify(b) for object equality in other places. You can read more about it here How to determine equality for two JavaScript objects?

Community
  • 1
  • 1
pyanzin
  • 120
  • 3
  • 10
  • Thanks for the answer. I can use http://lodash.com/ or any other library available on https://npmjs.org/. So I could replace `distinct()` with `_.isEqual()` from Lo-Dash? – John B. Jan 26 '14 at 19:48
  • `distinct` works properly here, and you can stay it as is. You can replace it with `something that takes array as parameter and returns unique values` – pyanzin Jan 26 '14 at 20:01