underscorejs:如何从对象数组中的合并属性中获取唯一类别

时间:2012-03-15 11:32:56

标签: unique underscore.js

简单问题的难题:)

说我们有一本书清单 它们属于不同的类别,这些类别是本书中的数组属性

我们希望将这个json转换为一个独特类别列表,其中包含类别

下的书籍

首先关闭:json:

[
    {
        "description":"book 1 description",
        "title":"book 1 title",
        "id":"4",
        "logo":"",
        "image":"",
        "categories":[
            {
                "id":"1",
                "title":"Logistiek"
            },{
                "id":"2",
                "title":"Finances"
            }
        ]
    },
    {
        "description":"book 2 description",
        "title":"book 2 title",
        "id":"1",
        "logo":"",
        "image":"",
        "categories":[
            {
                "id":"3",
                "title":"Telecom"
            }
        ]
    },
    {
        "description":"book 3 description",
        "title":"book 3 title",
        "id":"2",
        "logo":"",
        "image":"",
        "categories":[
            {
                "id":"3",
                "title":"Telecom"
            }
        ]
    },
    {
        "description":"book 4 description",
        "title":"book 4 title",
        "id":"3",
        "logo":"",
        "image":"",
        "categories":[
            {
                "id":"2",
                "title":"Finances"
            }
        ]
    }
]

现在我自己管理的是:

我首先绘制了所有类别:

var data = {} // lets say all json is inhere...
var res = _(data).map(function(m){
    return m.categories;
});

这个我分为1个类别(因为现在它是每本书的数组。

res = _(res).flatten();

这给了我一个包含所有类别项目的数组,尽管它有两倍。 现在我还没有比这更进一步。

我尝试在展平之前使用union方法但是没有帮助 我在更大的阵列上尝试了uniq,但我认为我必须将它们分解为单独的数组才能使uniq工作

我很难从这一类别中获取独特的价值。 之后我可以设法在类别

下添加书籍

如果有人得到了一些想法,或者告诉我,我这样做完全错了:)请告诉我,如果我可以做得更短或更好的表现,可以通过另一个方向随意扔给我。

UPDATE1

好吧,现在我进一步了,但我很确定它不理想,(太多步骤,我觉得获得一个独特的列表可能会比这些步骤更快)

// get all categorie arrays
var res = _(data).map(function(m){
    return m.categories;
});

// flatten them
res = _(res).flatten();

// reduce to unique ID array
var catIds = _(res).pluck('id');
catIds = _(catIds).uniq();

// from here on create an array with unique categories
var cats = [];

_(catIds).each(function(cId){
    var s = _(res).filter(function(c){
        return c.id === cId;
    });
    cats.push(_(s).first());
});

我可以更快地做到这一点吗?

见jsFiddle在行动...... http://jsfiddle.net/saelfaer/JVxGm/

更新2

好的,我得到了更多,感谢你们下面的帮助, 但我仍然觉得使用2个eaches并不是达到目的的最好方法。

var json = [] // lets say the above json is in this variable.
var books = _(json).map(function(book) {
    var cats = book.categories;
    delete book.categories;
    return _(cats).map(function(cat) {
        return _({}).extend(book, { category: cat });
    });
});
books = _(books).flatten();

var booksPerCategory = [];
_(books).each(function(book){
    if(!_(booksPerCategory).any(function(cat){
        return cat.id === book.category.id;
    }))
    { booksPerCategory.push(book.category); }
});

_(booksPerCategory).each(function(cat){
    var mods = _(books).filter(function(book){
        return book.category.id === cat.id;
    });
    cat['modules'] = mods;
});

你可以看到我想要收到的东西:booksByCategory数组 以及我从下面得到的帮助:书籍对象 在这个jsfiddle:http://jsfiddle.net/saelfaer/yWCgt/

2 个答案:

答案 0 :(得分:1)

这里有一些帮助,它不能解决你的整个问题,但我认为你应该可以从那里拿走它;)

关键是使用groupBy。像下面这样的东西应该给你一个良好的开端!

_.groupBy(data, function (book) { 
  return _.map(book.categories, function (category) { 
    return category.id;
  });
});

答案 1 :(得分:0)

你可以这样做:

var books = ​_(json).map(function(book) {
    var cats = book.categories;
    delete book.categories;
    return _(cats).map(function(cat) {
        return _({}).extend(book, { category: cat.id });
    });
});
books = _(books).flatten();
books = _(books).groupBy(function(book) { return book.category });

演示:http://jsfiddle.net/ambiguous/jaL8n/

如果你只想要每一本书的属性片段,那么你可以调整它:

return _({}).extend(book, { category: cat.id });

相应;例如,如果您只想要标题和类别ID:

return _(cats).map(function(cat) {
    return {
        category: cat.id,
        title:    book.title
    };
});

演示:http://jsfiddle.net/ambiguous/EFLDR/

您不需要显式生成一组唯一的类别ID,您只需要适当地排列数据,并且所有内容都会自行消失(这与功能方法相同)。


更新:根据评论,我认为您只需要在reduce之后添加flatten而不是groupBy

var books = _(json).map(function(book) {
    var cats = book.categories;
    delete book.categories;
    return _(cats).map(function(category) {
        return {
            category: category,
            book:     book
        };
    });
});
books = _(books).flatten();
books = _(books).reduce(function(h, b) {
    if(!h[b.category.id])
        h[b.category.id] = _(b.category).extend({ modules: [ ] });
    h[b.category.id].modules.push(b.book);
    return h;
}, { });

演示:http://jsfiddle.net/ambiguous/vJqTz/

您还可以使用chain和一些命名函数来使其更易于阅读:

function rearrange(book) {
    var cats = book.categories;
    delete book.categories;
    return _(cats).map(function(category) {
        return {
            category: category,
            book:     book
        };
    });
}

function collect_into(h, b) {
    if(!h[b.category.id])
        h[b.category.id] = _(b.category).extend({ modules: [ ] });
    h[b.category.id].modules.push(b.book);
    return h;
}

var books = _.chain(json).
    map(rearrange).
    flatten().
    reduce(collect_into, { }).
    value();

演示:http://jsfiddle.net/ambiguous/kSU7v/