我有一个对象数组,在其中按键(下面的group
)进行了排序,以便所有具有group
相同值的对象在data
的索引中彼此相邻。例如:
var data = [{foo: "cat", group:"house"},
{foo: "cat", group: "house"},
{foo: "cat", group: "tree"},
{foo: "dog", group: "tree"},
{foo: "dog", group: "car"}];
我试图在data
数组中排列对象的顺序,同时将顺序保留在键group
的值之内。换句话说,我正在尝试改组data
中的对象组,而不是单个对象。虽然我知道如何重新排列数组中的对象,但是我不知道如何重新排列数组中的对象组。
我的想法是,可能有一种方法可以使用仅当组更改时组的值才会更改的事实。
您在这里有一个有趣的问题。我只是wrote about this recently,所以如果您对此答案中提出的想法感兴趣,请点击该链接-
const randInt = (n = 0) =>
Math.floor(Math.random() * n)
const { empty, contramap, concat } =
Comparison
const sortByGroup =
contramap(empty, x => x.group)
const sortByRand =
contramap(empty, _ => randInt(3) - 1) // -1, 0, 1
直观上,我们使用contramap(empty, ...)
进行新的比较(分类)。 concat
是我们用来将一个比较与另一个比较结合在一起的方法-
// sort by .group then sort by rand
const mySorter =
concat(sortByGroup, sortByRand)
我们的比较直接插入Array.prototype.sort
-
const data =
[ { name: "Alice", group: "staff" }
, { name: "Monty", group: "client" }
, { name: "Cooper", group: "client" }
, { name: "Jason", group: "staff" }
, { name: "Farrah", group: "staff" }
, { name: "Celeste", group: "guest" }
, { name: "Briana", group: "staff" }
]
console.log("first", data.sort(mySorter)) // shuffle once
console.log("second", data.sort(mySorter)) // shuffle again
在输出中,我们看到按group
分组的项目,然后将其随机化-
// first
[ { name: "Cooper", group: "client" }
, { name: "Monty", group: "client" }
, { name: "Celeste", group: "guest" }
, { name: "Alice", group: "staff" }
, { name: "Jason", group: "staff" }
, { name: "Farrah", group: "staff" }
, { name: "Briana", group: "staff" }
]
// second
[ { name: "Monty", group: "client" }
, { name: "Cooper", group: "client" }
, { name: "Celeste", group: "guest" }
, { name: "Alice", group: "staff" }
, { name: "Jason", group: "staff" }
, { name: "Farrah", group: "staff" }
, { name: "Briana", group: "staff" }
]
最后,我们实现Comparison
-
const Comparison =
{ empty: (a, b) =>
a < b ? -1
: a > b ? 1
: 0
, contramap: (m, f) =>
(a, b) => m(f(a), f(b))
, concat: (m, n) =>
(a, b) => Ordered.concat(m(a, b), n(a, b))
}
const Ordered =
{ empty: 0
, concat: (a, b) =>
a === 0 ? b : a
}
展开下面的代码段,以在您自己的浏览器中验证结果。运行程序[[multiple times,以查看结果始终按group
排序,然后随机化-
const Comparison =
{ empty: (a, b) =>
a < b ? -1
: a > b ? 1
: 0
, contramap: (m, f) =>
(a, b) => m(f(a), f(b))
, concat: (m, n) =>
(a, b) => Ordered.concat(m(a, b), n(a, b))
}
const Ordered =
{ empty: 0
, concat: (a, b) =>
a === 0 ? b : a
}
const randInt = (n = 0) =>
Math.floor(Math.random() * n)
const { empty, contramap, concat } =
Comparison
const sortByGroup =
contramap(empty, x => x.group)
const sortByRand =
contramap(empty, _ => randInt(3) - 1) // -1, 0, 1
const mySorter =
concat(sortByGroup, sortByRand) // sort by .group then sort by rand
const data =
[ { name: "Alice", group: "staff" }
, { name: "Monty", group: "client" }
, { name: "Cooper", group: "client" }
, { name: "Jason", group: "staff" }
, { name: "Farrah", group: "staff" }
, { name: "Celeste", group: "guest" }
, { name: "Briana", group: "staff" }
]
console.log(JSON.stringify(data.sort(mySorter))) // shuffle once
console.log(JSON.stringify(data.sort(mySorter))) // shuffle again
代替小改进
sortByGroup
之类的硬编码分类器,我们可以进行parameterized比较,sortByProp
-
const sortByProp = (prop = "")
contramap(empty, (o = {}) => o[prop])
const sortByFullName =
concat
( sortByProp("lastName") // primary: sort by obj.lastName
, sortByProp("firstName") // secondary: sort by obj.firstName
)
data.sort(sortByFullName) // ...
定义单独的为什么要使用模块?
Comparison
模块的好处很多。该模块使我们能够轻松建模复杂的排序逻辑-const sortByName =
contramap(empty, x => x.name)
const sortByAge =
contramap(empty, x => x.age)
const data =
[ { name: 'Alicia', age: 10 }
, { name: 'Alice', age: 15 }
, { name: 'Alice', age: 10 }
, { name: 'Alice', age: 16 }
]
按name
排序,然后按age
-
data.sort(concat(sortByName, sortByAge)) // [ { name: 'Alice', age: 10 } // , { name: 'Alice', age: 15 } // , { name: 'Alice', age: 16 } // , { name: 'Alicia', age: 10 } // ]
按age
排序,然后按name
-
data.sort(concat(sortByAge, sortByName)) // [ { name: 'Alice', age: 10 } // , { name: 'Alicia', age: 10 } // , { name: 'Alice', age: 15 } // , { name: 'Alice', age: 16 } // ]
并且毫不费力地reverse
任何分拣器。在这里,我们按name
排序,然后按age
-反向排序
const Comparison = { // ... , reverse: (m) => (a, b) => m(b, a) } data.sort(concat(sortByName, reverse(sortByAge))) // [ { name: 'Alice', age: 16 } // , { name: 'Alice', age: 15 } // , { name: 'Alice', age: 10 } // , { name: 'Alicia', age: 10 } // ]
我们的功能原理
Comparison
模块灵活而可靠。这使我们能够以类似公式的方式编写分类器-// this...
concat(reverse(sortByName), reverse(sortByAge))
// is the same as...
reverse(concat(sortByName, sortByAge))
并且类似地使用concat
表达式-
// this... concat(sortByYear, concat(sortByMonth, sortByDay)) // is the same as... concat(concat(sortByYear, sortByMonth), sortByDay) // is the same as... nsort(sortByYear, sortByMonth, sortByDay)
由于我们的比较可以合并以创建更复杂的比较,因此我们可以根据任意数量的因素进行有效排序。例如,对日期对象排序需要进行三个比较:多分类
year
,month
和day
。由于功能原理,我们的concat
和empty
做了所有艰苦的工作-const Comparison =
{ // ...
, nsort: (...m) =>
m.reduce(Comparison.concat, Comparison.empty)
}
const { empty, contramap, reverse, nsort } =
Comparison
const data =
[ { year: 2020, month: 4, day: 5 }
, { year: 2018, month: 1, day: 20 }
, { year: 2019, month: 3, day: 14 }
]
const sortByDate =
nsort
( contramap(empty, x => x.year) // primary: sort by year
, contramap(empty, x => x.month) // secondary: sort by month
, contramap(empty, x => x.day) // tertiary: sort by day
)
现在我们可以按year
,month
,day
-]进行排序
data.sort(sortByDate) // [ { year: 2019, month: 11, day: 14 } // , { year: 2020, month: 4, day: 3 } // , { year: 2020, month: 4, day: 5 } // ]
同样容易按year
,month
,day
-]进行反向排序>
data.sort(reverse(sortByDate)) // [ { year: 2020, month: 4, day: 5 } // , { year: 2020, month: 4, day: 3 } // , { year: 2019, month: 11, day: 14 } // ]
要运行reverse
和nsort
示例,请遵循original post👈