配列に重複するオブジェクトがある場合に、重複するオブジェクトを取り除きつつも、オブジェクト毎にいくつ重複するオブジェクトがあったか知りたいことがあります。
重複するオブジェクトの数を count
プロパティに代入したい場合は以下のように書けます。condition
はオブジェクトが重複する条件を定義します。
Array.protptype.reduce((a, b) => {
const duplicated = a.find(/* condition */)
duplicated ? duplicated.count += 1 : a.push({...b, count: 1})
return a
}, [])
また、特に連続する重複するオブジェクトのみを対象とする場合は以下のように書けます。
予め重複するオブジェクトが連続することが分かっている場合は、こちらの方が計算量が線形なのでよいと思います。
Array.protptype.reduce((a, b) => {
const last = a[a.length - 1] // a が [] の場合は last は undefined になります。
last && (/* condition... */) ? last.count += 1 : a.push({...b, count: 1})
return a
}, [])
具体的な使い方をみていきます。例えば、以下のようなデータがある場合を想定します。
type person = {
name: string,
age: number
}
const data: person[] = [
{name: "aaa", age: 18,},
{name: "bbb", age: 20,},
{name: "bbb", age: 21,},
{name: "ccc", age: 21,},
{name: "bbb", age: 20,},
]
name
と age
のいずれも重複するオブジェクトを取り除きつつ数えたい場合
data.reduce((a: (person & {count: number})[], b) => {
const duplicated = a.find(e => e.name == b.name && e.age == b.age)
duplicated ? duplicated.count += 1 : a.push({...b, count: 1})
return a
}, [])
/*
[
{name: "aaa", age: 18, count: 1},
{name: "bbb", age: 20, count: 2},
{name: "bbb", age: 21, count: 1},
{name: "ccc", age: 21, count: 1},
]
*/
name
が重複する連続するオブジェクトを取り除きつつ数えたい場合
data.reduce((a: (person & {count: number})[], b) => {
const last = a[a.length - 1]
last && b.name === last.name ? last.count += 1 : a.push({...b, count: 1})
return a
}, [])
/*
[
{name: "aaa", age: 18, count: 1},
{name: "bbb", age: 20, count: 2},
{name: "ccc", age: 21, count: 1},
{name: "bbb", age: 20, count: 1},
]
*/
他の書き方もあればフィードバックしてください。
そういえば、a
に (person & {count: number}[])
型を与えない場合、a
は never[]
型と推論されました。処理の内容から推論できると思ったのですが、どういうわけなんでしょう。