对于表示为Javascript对象数组的表,SQL'JOIN'的实用模拟是什么? Javascript Array.join
和D3.js'd3.merge`的概念不同。
E.g。 SELECT * FROM authors LEFT JOIN books ON authors.id = books.author_id
?
第一张表:
var authors =
[ { id: 1, name: 'adam'},
{ id: 2, name: 'bob'},
{ id: 3, name: 'charlie'}, ...
]
第二张表:
var books =
[ { author_id: 1, title: 'Coloring for beginners'},
{ author_id: 1, title: 'Advanced coloring'},
{ author_id: 2, title: '50 Hikes in New England'},
{ author_id: 2, title: '50 Hikes in Illinois'},
{ author_id: 3, title: 'String Theory for Dummies'}, ...
]
这些表是使用D3.js d3.csv()
从CSV加载的,所以D3.js已经开放给其他库,但通常更喜欢直接编码,如果不是太远的话。
我看到使用RethinkDB的Native way to merge objects in Javascript,这似乎超过了顶部,但这就是主意。
答案 0 :(得分:11)
基本上,像这样:
// first, build an easier lookup of author data:
var authormap = {};
authors.forEach(function(author) {authormap[author.id] = author;});
// now do the "join":
books.forEach(function(book) {
book.author = authormap[book.author_id];
});
// now you can access:
alert(books[0].author.name);
答案 1 :(得分:2)
您可以使用Alasql JavaScript SQL库执行此操作:
var res = alasql('SELECT * FROM ? authors \
LEFT JOIN ? books ON authors.id = books.author_id',[authors, books]);
使用jsFiddle中的数据尝试this example。
您也可以将CSV数据直接加载到SQL表达式中:
alasql('SELECT * FROM CSV("authors.csv", {headers:true}) authors \
LEFT JOIN CSV("books.csv", {headers:true}) books \
ON authors.id = books.author_id',[], function(res) {
console.log(res);
});
答案 2 :(得分:1)
这是受@ Niet的回答启发的。
我遇到了重复数据的问题,所以我添加了在加入之前在查找表中克隆记录的步骤。
var authors = [{
id: 1,
name: 'adam'
}, {
id: 2,
name: 'bob'
}, {
id: 3,
name: 'charlie'
}];
var books = [{
author_id: 1,
title: 'Coloring for beginners'
}, {
author_id: 1,
title: 'Advanced coloring'
}, {
author_id: 2,
title: '50 Hikes in New England'
}, {
author_id: 2,
title: '50 Hikes in Illinois'
}, {
author_id: 3,
title: 'String Theory for Dummies'
}];
function joinTables(left, right, leftKey, rightKey) {
rightKey = rightKey || leftKey;
var lookupTable = {};
var resultTable = [];
var forEachLeftRecord = function (currentRecord) {
lookupTable[currentRecord[leftKey]] = currentRecord;
};
var forEachRightRecord = function (currentRecord) {
var joinedRecord = _.clone(lookupTable[currentRecord[rightKey]]); // using lodash clone
_.extend(joinedRecord, currentRecord); // using lodash extend
resultTable.push(joinedRecord);
};
left.forEach(forEachLeftRecord);
right.forEach(forEachRightRecord);
return resultTable;
}
var joinResult = joinTables(authors, books, 'id', 'author_id');
console.log(joinResult);
结果是
[
{
"id": 1,
"name": "adam",
"author_id": 1,
"title": "Coloring for beginners"
},
{
"id": 1,
"name": "adam",
"author_id": 1,
"title": "Advanced coloring"
},
{
"id": 2,
"name": "bob",
"author_id": 2,
"title": "50 Hikes in New England"
},
{
"id": 2,
"name": "bob",
"author_id": 2,
"title": "50 Hikes in Illinois"
},
{
"id": 3,
"name": "charlie",
"author_id": 3,
"title": "String Theory for Dummies"
}
]
答案 3 :(得分:1)
我也在寻找这样的东西,并使用一些函数式编程解决了它。我已经冒昧地为你的初始数组添加了几个对象,以便处理" NULL"例。
EndDialog()
所以现在你有一本没有作者的书和没有书的作者。我的解决方案如下:
var books = [
{author_id: 1, title: 'Coloring for beginners'},
{author_id: 1, title: 'Advanced coloring'},
{author_id: 2, title: '50 Hikes in New England'},
{author_id: 2, title: '50 Hikes in Illinois'},
{author_id: 3, title: 'String Theory for Dummies'},
{author_id: 5, title: 'Map-Reduce for Fun and Profit'}
];
var authors = [
{id: 1, name: 'adam'},
{id: 2, name: 'bob'},
{id: 3, name: 'charlie'},
{id: 4, name: 'diane'}
];
map方法使用var joined = books.map(function(e) {
return Object.assign({}, e, authors.reduce(function(acc, val) {
if (val.id == e.author_id) {
return val
} else {
return acc
}
}, {}))
});
遍历books
的每个元素,并返回一个数组,其元素是e
的合并对象,其对应的对象位于e
阵列。 authors
在不修改原始对象的情况下照顾merge。
通过在Object.assign({},a,b)
数组上应用reduce方法,可以找到e
中每个books
的相应对象。从空对象authors
的初始值开始(这是reduce的第二个参数 - 它也可能是一个空作者,如{}
),reduce方法遍历{id:'', name ''}
的元素1}}使用authors
并返回最终在val
中的对象。如果在图书acc
和作者author_id
之间找到匹配项,则整个匹配的作者对象最终会在id
中,并最终由acc
返回。< / p>
n.b。 - 使用reduce并不是那么有效,因为一旦找到匹配就有no way to break out of reduce loop,它将继续到数组的末尾
答案 4 :(得分:1)
我在这种情况下略有不同,我需要在两个数据集之间的一致键上连接两个数组。在我的数据集增长时,JSON对象变得过于笨重,因此连接这两个数组要容易得多:
var data = new Array(["auth1","newbook1","pubdate1"],["auth2","newbook2","pubdate2"]);
var currData = new Array(["auth1","newbook3","pubdate3"],["auth2","newbook3","pubdate4"]);
var currDataMap = currData.map(function(a){return a[0];});
var newdata = new Array();
for(i=0;i<data.length;i++){
if(currDataMap.indexOf(data[i][0])>-1){
newdata[i] = data[i].concat(currData[currDataMap.indexOf(data[i][0])].slice(1));
}
}
输出:
[
[auth1, newbook1, pubdate1, newbook3, pubdate3],
[auth2, newbook2, pubdate2, newbook3, pubdate4]
]
就我而言,我还需要删除没有新数据的行,因此您可能希望排除条件。
答案 5 :(得分:0)
不是最优雅的代码,但是如果您需要的话,我认为它很简单。允许进行INNER,LEFT和RIGHT连接。
您只需要将函数复制并粘贴到代码中即可获得正确的输出。例子在底部
def create_account
# You can assign the 'get' method results to a var if you want
puts "Enter your full name"
get_full_name = gets.chomp
puts 'Enter your email address'
get_email = gets.chomp
puts 'Enter your desired username.'
get_username = gets.chomp
## It will only enter the lop if such a customer already exists.
## Let's add one:
customer = Customer.create(
first_last_name: get_full_name,
email_address: get_email,
username: get_username,
password: "password")
if customer.errors.full_messages.empty?
puts "******successfully created customer*******"
puts customer
else
puts "*****ok we could not create the customer*****"
end
while Customer.exists?(username: get_username) do
puts "This username is already taken. Please enter a different one"
get_username = gets.chomp
break if !Customer.exists?(username: get_username)
end
puts "Please enter password"
get_password = gets.chomp
customer = Customer.create(
first_last_name: get_full_name,
email_address: get_email,
username: get_username,
password: get_password)
end