underscorejs: как получить уникальные категории из объединенных свойств в массиве объектов - PullRequest
1 голос
/ 15 марта 2012

сложное название для простого выпуска:)

скажем, у нас есть список книг они находятся в разных категориях, эти категории являются свойством массива в книге

мы хотим преобразовать этот 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();

это дает мне массив всех элементов категории, хотя в нем есть двойники. теперь я еще далеко не уйду.

Я пытался использовать метод объединения перед выравниванием, но это не помогло я попробовал 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 ]

1 голос
/ 15 марта 2012

Вот некоторая помощь, она не решит всю вашу проблему, но я думаю, что вы сможете взять ее оттуда;)

Ключ в использовании groupBy.Что-то вроде следующего должно дать вам хорошее начало!

_.groupBy(data, function (book) { 
  return _.map(book.categories, function (category) { 
    return category.id;
  });
});
0 голосов
/ 16 марта 2012

Вы можете сделать что-то вроде этого:

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 });

соответственно;например, если вам нужны заголовки и идентификаторы категорий:

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

Демо: http://jsfiddle.net/ambiguous/EFLDR/

Вам не нужно явно генерировать набор уникальных идентификаторов категорий, простонеобходимо правильно расположить ваши данные, и все выпадает само по себе (как это происходит с функциональными подходами).


ОБНОВЛЕНИЕ : На основании комментариев, я думаю, вам просто нужнодобавить 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/

...