Объединение перекрывающихся наборов данных

Учитывая несколько наборов данных, которые могут/не могут перекрываться в одном или нескольких столбцах, я хочу динамически объединить наборы данных вместе.

Есть ли библиотека или фрагмент кода, который будет объединять наборы данных таким образом? Как насчет того, чтобы просто использовать один столбец в качестве ключа?

ПРИМЕР. Объединение двух наборов данных с использованием нескольких столбцов в качестве ключей (BookTitle, Author)

Ввод, набор данных 1

BookTitle, Author, Publisher
title1, author1, publisher1
title2, author2, publisher2
title3, author3, publisher3

Ввод, набор данных 2

BookTitle, Author, NumPages
title4, author4, numPages4
title7, author7, numPages7
title5, author5, numPages5
title3, author33, numPages3
title2, author2, numPages2

Вывод, измененный набор данных

BookTitle, Author, Publisher, NumPages
title1, author1, publisher1, _null_
title2, author2, publisher2, numPages2
title3, author3, publisher3, _null_
title4, author4, _null_, numPages4
title5, author5, _null_, numPages5
title7, author7, _null_, numPages7
title3, author33, _null_, numPages3

Я провел некоторое исследование, и сразу ничего полезного не обнаружилось (в основном об однократном слиянии объектов JSON в одной и той же структуре (т. е. добавление данных, а не слияние отдельных наборов данных)).

Я ищу Java/JavaScript, используя данные JSON/XML/CSV (в порядке предпочтения), но приму другие языки, предполагая, что эти алгоритмы могут быть перенесены.

Я также рассмотрю возможность принятия примеров, когда это делается только для одного столбца.


person kwah    schedule 26.07.2014    source источник


Ответы (1)


Ну, я бы не стал искать библиотеку для чего-то такого простого. Вместо этого попробуйте создать решение самостоятельно.

Вы можете сначала JSON.parse() преобразовать любые строки в объекты. Затем вы можете передать оба этих объекта в функцию, которая выглядит примерно так.

function mergeSets(first, second) {
    var result = first;
    second.forEach(function (item, index, array) {
        var resultIndex = contains(result, item);
        if (resultIndex === -1) {
            result.push(item);
        } else {
            result[resultIndex].numPages = item.numPages;
        }
    });
    return result;
}

Обратите внимание, что mergeSets() вызывает contains(), что по существу выглядит следующим образом.

function contains(set, object) {
    var solution = -1;
    set.forEach(function (item, index, array) {
        if (item.bookTitle == object.bookTitle && item.author == object.author) {
            solution = index;
        }
    });
    return solution;
}

Это действительно не так уж сложно, как вы можете видеть. Извините за некоторые имена переменных. Это было написано наспех. Кроме того, вы упоминаете в своем примере результирующего набора, что хотели бы, чтобы недоступные поля отображались как null, что неуместно, поскольку null обычно указывает на пустую ссылку. Вместо этого я проигнорировал их. Доступ к этим полям объектов в массиве, у которых их нет, приведет к undefined, что имеет смысл.

Кроме того, ниже приведены ограничения кода в скрипке. Вы можете отредактировать его, чтобы снять эти ограничения и сделать его более надежным.

  1. Он привязан к формату данных, который вы упомянули в своем вопросе. Чтобы заставить его работать для произвольных наборов, вы можете проверить наличие свойства, используя Object.hasOwnProperty() в цикле for-in, и добавить необходимые, что приведет к слиянию.

  2. В любом случае он не обрабатывает дубликаты внутри наборов.

http://jsfiddle.net/x5Q5g/

Изменить: О! И, кстати, код — это JavaScript, а формат данных может быть JSON, если вы используете JSON.parse() и JSON.stringify().

Изменить. Следующие обновления отменяют первое ограничение, упомянутое выше. Обратите внимание, что вам нужно явно передать ключ для сравнения на основе.

function contains(set, object, key) {
    var solution = -1;
    set.forEach(function (item, index, array) {
        if (item[key] === object[key]) {
            solution = index;
        }
    });
    return solution;
}

function mergeSets(first, second, key) {
    var result = first;
    second.forEach(function (item, index, array) {
        var resultIndex = contains(result, item, key);
        if (resultIndex === -1) {
            result.push(item);
        } else {
            result[resultIndex].numPages = item.numPages;
            for (var property in item) {
                if (item.hasOwnProperty(property)) {
                    if (!result[resultIndex].hasOwnProperty(property)) {
                        result[resultIndex].property = item.property;
                    }
                }
            }
        }
    });
    return result;
}

var solution = mergeSets(firstSet, secondSet, "bookTitle");
console.log(solution);

http://jsfiddle.net/s6HqL/

Последнее обновление: вот как вы можете заставить его принимать любое количество ключей. Я забыл, что вам требуется поддержка нескольких ключей. Прости!

Вам нужно изменить следующее.

function contains(set, object, keys) {
    var solution = -1;
    set.forEach(function (item, index, array) {
        var selfItem = item;
        var allKeys = keys.every(function (item, index, array) {
            if (selfItem[item] === object[item]) {
                return true;
            }
        });
        if (allKeys) {
            solution = index;
        }
    });
    return solution;
}

function mergeSets(first, second) {
    var result = first;
    var keys = Array.prototype.slice.call(arguments, 2);
    second.forEach(function (item, index, array) {
        var resultIndex = contains(result, item, keys);
        if (resultIndex === -1) {
            result.push(item);
        } else {
            for (var property in item) {
                if (item.hasOwnProperty(property)) {
                    if (!result[resultIndex].hasOwnProperty(property)) {
                        var hello = result[resultIndex];
                        hello[property] = item[property];
                    }
                }
            }
        }
    });
    return result;
}

var solution = mergeSets(firstSet, secondSet, "bookTitle", "author");
console.log(solution);

http://jsfiddle.net/s6HqL/3/

Эта последняя скрипка и код над ней завершены. Без всяких ссылок! И является общим. Будет работать с любым количеством ключей в качестве аргументов.

person Siddharth    schedule 26.07.2014
comment
Это выглядит как интересная отправная точка, спасибо. Часть, не решенная этим решением, заключается в том, что структура наборов данных и столбцы, в которых эти наборы данных будут объединены, являются динамическими (т. е. неизвестными до времени выполнения / предоставленными пользователем). Это может быть просто случай извлечения их из параметров..? - person kwah; 26.07.2014
comment
Слишком поздно редактировать мой комментарий, но комментарий «библиотека» предназначен для намека на то, что я ищу что-то общее, а не кодированное для конкретных наборов данных. Я посмотрю на редактирование вопроса, чтобы быть более ясным :). - person kwah; 26.07.2014
comment
Позвольте мне отредактировать мое решение и сделать его общим для вас. Подожди. - person Siddharth; 26.07.2014
comment
Я проголосовал за вас, но, поскольку он не завершен, я не решаюсь отметить его как правильный ответ (например, все еще есть ссылки на .numPages). Я поиграю, чтобы сделать его еще более общим. Предполагая, что за это время не было опубликовано более полного ответа, я буду рад отметить это как правильное, как только закончу (и поделюсь скрипкой), поскольку вы значительно помогли :) - person kwah; 26.07.2014
comment
Упс. Я забыл удалить эту строку. Без него работает! Вам не нужна ссылка на numPages. Проверьте последнюю скрипку. - person Siddharth; 26.07.2014
comment
О круто, ура! Принято, поскольку вы отвечаете на вопрос, который я задал! :) Вы заслуживаете большего, чем просто один положительный голос/принятие представителя за то, что вы отзывчивы и полезны! - person kwah; 26.07.2014
comment
Ах. Не беспокойтесь об этом. Всегда пожалуйста. Это то, о чем этот сайт. - person Siddharth; 26.07.2014