我正在使用一个返回JSON数据的应用程序接口,我想按时间顺序对数据进行排序--按对象的releaseDate (整数)属性排序。
数据示例:
let all_albums = [
{
"releaseDate": 1461913200000,
"critic_rating": 69,
"user_rating": 57,
"average_rating": 63
},
{
"releaseDate": 1380006000000,
"critic_rating": 79,
"user_rating": 75,
"average_rating": 77
},
{
"releaseDate": 1321344000000,
"critic_rating": 78,
"user_rating": 79,
"average_rating": 78.5
},
]
如果我将其复制/粘贴到项目之外的单独文件中,则可以使用以下命令对其进行排序:
let sortedAlbums = all_albums.sort(function (a, b) {
return a.releaseDate - b.releaseDate;
});
但是,如果我尝试将其实现到我的项目中,我可以用同样的方式遍历JSON,以同样的方式打印到控制台,所有的数据都与我上面演示的完全相同--但是我不能对这个数据集进行排序。
我来自python背景,所以异步的世界对我来说是新的。这有关系吗?或者,如何对刚从API调用的对象进行排序?
感谢大家到目前为止的评论。我重构了一些变量,根据你的建议清理了代码。尽管如此,当直接从API调用时,问题仍然存在。下面是示例调用的完整代码(尽管带有被审查的mashape键)
$(function () {
return $.ajax({
url: 'https://api-marcalencc-metacritic-v1.p.mashape.com/person/drake/album',
type: 'get',
dataType: 'json',
headers: {
"X-Mashape-Key": "----------------------------",
"Accept": "application/json"
},
success: function (data) {
handleDiscography(data);
},
})
});
const allAlbums = [];
function Album(releaseDate) {
this.releaseDate = new Date(releaseDate).getTime();
}
function addAlbum(releaseDate) {
const a = new Album(releaseDate);
allAlbums.push(a);
}
function handleDiscography(data) {
const album = data[0].CreditMediaPairItems;
$.each(album, function (key, val) {
const releaseDate = val.Item.ReleaseDate;
addAlbum(releaseDate);
});
return data
}
const sortedAlbums = allAlbums.sort((a, b) => a.releaseDate - b.releaseDate);
console.log(sortedAlbums);
发布于 2018-02-15 14:01:23
这个问题实际上是一个异步问题。
ajax请求的success
回调中的代码在以下代码之后执行:
const sortedAlbums = allAlbums.sort((a, b) => a.releaseDate - b.releaseDate);
console.log(sortedAlbums);
Array.prototype.sort
对数组中的元素进行原地排序并返回该数组。因此,在调用sort
之后,sortedAlbums
和allAlbums
都引用相同的数组。
因为当时没有请求任何数据,所以在空数组上调用sort
,并且不对任何内容进行排序。
下一个问题是,console.log(sortedAlbums);
不会显示sortedAlbums
在被记录时的内容,而是在开发人员工具中第一次检查它时的内容。因此,当您查看它时,请求已经完成,并且现在引用与sortedAlbums
相同的对象的allAlbums
已经被填充。但这是在您对空数组进行排序之后发生的。
最简单的方法是将排序和日志放在success
回调中:
$.ajax({
url: 'https://api-marcalencc-metacritic-v1.p.mashape.com/person/drake/album',
type: 'get',
dataType: 'json',
headers: {
"X-Mashape-Key": "----------------------------",
"Accept": "application/json"
},
success: function(data) {
handleDiscography(data);
allAlbums.sort((a, b) => a.releaseDate - b.releaseDate);
console.log(allAlbums);
}
})
一个更干净的解决方案看起来像这样:
function Album(releaseDate) {
this.releaseDate = new Date(releaseDate).getTime();
}
function handleDiscography(data) {
const album = data[0].CreditMediaPairItems;
return album.map(val => new Album(val.Item.ReleaseDate))
}
function getAlbumData() {
return $.ajax({
url: 'https://api-marcalencc-metacritic-v1.p.mashape.com/person/drake/album',
type: 'get',
dataType: 'json',
headers: {
"X-Mashape-Key": "----------------------------",
"Accept": "application/json"
}
})
.then(data => handleDiscography)
}
getAlbumData()
.then(allAlbums => {
allAlbums.sort(function(a, b) {
return a.releaseDate - b.releaseDate;
});
console.log(allAlbums)
})
在本例中,您完全摆脱了全局对象,这是应该始终避免的,特别是在使用异步代码时。
发布于 2018-02-15 11:38:51
我稍微清理了一下你的示例代码,但在功能上是一样的。它排序很好。
const allAlbums = [
{
'releaseDate': 1461913200000,
'critic_rating': 69,
'user_rating': 57,
'average_rating': 63
},
{
'releaseDate': 1380006000000,
'critic_rating': 79,
'user_rating': 75,
'average_rating': 77
},
{
'releaseDate': 1321344000000,
'critic_rating': 78,
'user_rating': 79,
'average_rating': 78.5
},
];
const sortedAlbums = allAlbums.sort((a, b) => a.releaseDate - b.releaseDate);
console.log(sortedAlbums);
输出:
[
{
"releaseDate": 1321344000000,
"critic_rating": 78,
"user_rating": 79,
"average_rating": 78.5
},
{
"releaseDate": 1380006000000,
"critic_rating": 79,
"user_rating": 75,
"average_rating": 77
},
{
"releaseDate": 1461913200000,
"critic_rating": 69,
"user_rating": 57,
"average_rating": 63
}
]
这里有一些样式技巧,因为您是Python程序员。虽然没有官方的标准风格指南,但有一些一般性的建议。
使用单引号。仅限camelCase。两个空格,而不是制表符。
在处理数组和对象文字时,您几乎总是希望使用const
。仅当您将相同的命名变量重新赋值为不同的值时,才需要let
和var
。在处理数组和对象时,您可以自由地修改它们的属性,因为您不需要重新分配变量。
在声明函数时,90%的时间胖箭头语法将有助于防止处理this
的错误。另外10%的时间是胖箭头导致错误的时候。
发布于 2018-02-15 13:51:27
下面是你的代码应该是什么样子,加上一些注释。看起来您正在尝试使用一个全局变量allAlbums
,该变量是在带有异步结果的文档就绪事件中设置的。
但是您正在尝试在文档就绪事件处理程序之外使用该变量,因此现在访问它还为时过早,因为甚至没有尝试加载数据,而且即使加载数据是异步的,您也不能为其设置值,因为该值在将来可用(或者如果失败,则根本不能)。
您只能在document ready中访问allAlbums
,并将其设置为promise值,以便知道实际值何时可用。
如果allAlbums与其他代码共享,那么不要仅仅对它进行排序,因为排序会改变它,也许当您在其他地方使用它时,您不希望它以某种方式排序。如果您确实希望对其进行排序,请在.sort
之前删除.map(x=>x)
//All albums is the result of asynchronous action so better store it
// as a result of asynchronous action (promise)
var allAlbums = $.Deferred().resolve([]);
function Album(releaseDate) {
this.releaseDate = new Date(releaseDate).getTime();
}
const logSorted = (message,albums) =>
albums.then(
albums => albums.map(x=>x).sort((a, b) => a.releaseDate - b.releaseDate)
).then(
sortedAlbums=>console.log(message,sortedAlbums)
);
$(function () {
//set allAlbums to a promise
allAlbums = $.ajax({
url: 'https://api-marcalencc-metacritic-v1.p.mashape.com/person/drake/album',
type: 'get',
dataType: 'json',
headers: {
"X-Mashape-Key": "----------------------------",
"Accept": "application/json"
}
})
.then(
data =>
data[0].CreditMediaPairItems.map(
val =>
new Album(val.Item.ReleaseDate)
)
);
allAlbums.then(
albums=>
logSorted(
"all albums is set with results so should log sorted:",
albums
)
);
});
//it is kind of pointless to try and use all albums outside of he document ready
// because document ready is an event that you don't know when it happens unless
// code is inside document ready.
logSorted("all albums should be empty so sorted is empty too",allAlbums);
当你解决了异步问题,你仍然需要排序,下面是一个如何组合函数来构建多列排序的示例:
const sorter = getter => comparer => (a,b) =>
comparer(getter(a),getter(b));
const compareNumbers = (a,b)=>a-b;
const compareStrings = (a,b)=>a>b?1:(a<b)?-1:0;
const sortReleaseDate = sorter(x=>x.releaseDate)(compareNumbers);
const sortSomeStringValue = sorter(x=>x.someString)(compareStrings);
const sort = (sorters) => (a,b) => {
const recur = (index) =>{
if(index===sorters.length){
return 0;
}
const [sorter,direction] = sorters[index];
const result = sorter(a,b);
if(result!==0){
return result*direction;
}
return recur(index+1);
}
return recur(0);
};
//example on how to use:
const sampleArray = [
{someString:"a",releaseDate:3},
{someString:"a",releaseDate:2},
{someString:"a",releaseDate:1},
{someString:"b",releaseDate:3},
{someString:"b",releaseDate:2},
{someString:"b",releaseDate:1},
{someString:"c",releaseDate:3},
{someString:"c",releaseDate:2},
{someString:"c",releaseDate:1}
];
console.log(
"someString descending then releaseDate ascending",
JSON.stringify(
sampleArray
.map(x=>x)//copy so we won't change original
.sort(
sort([
[sortSomeStringValue,-1],//sort by by some string descending
[sortReleaseDate,1]//sort by release date ascending
])
),
undefined,
2
)
);
console.log(
"releaseDate descending then some string ascending",
JSON.stringify(
sampleArray
.map(x=>x)//copy so we won't change original
.sort(
sort([
[sortReleaseDate,-1],//sort by release date descending
[sortSomeStringValue,1]//sort by by some string ascending
])
),
undefined,
2
)
)
console.log(
"only releaseDate descending",
JSON.stringify(
sampleArray
.map(x=>x)//copy so we won't change original
.sort(
sort([
[sortReleaseDate,-1]//sort by release date descending
])
),
undefined,
2
)
)
https://stackoverflow.com/questions/48799558
复制相似问题