Если делать ровно то, что спрошено:
const roots = [];
for (const n of arr) {
if (!arr.some(m => m.id === n.root_id)) {
roots.push(n);
}
}
// или
const roots = arr.filter(function(n) {
return !this.has(n.root_id);
}, new Set(arr.map(n => n.id)));
// или
const roots = (function get(i = 0, n = arr[i]) {
return n
? [].concat(arr.every(m => m.id !== n.root_id) ? n : [], get(-~i))
: []
})();
Но вообще, дерево-то можно и собрать, результатом как раз будет массив искомых объектов (конечно, не без дополнения в виде вложенных массивов):
function createTree({
data,
key = 'id',
parentKey = 'parentId',
childrenKey = 'children',
}) {
const tree = Object.fromEntries(data.map(n => [
n[key],
{ ...n, [childrenKey]: [] },
]));
return Object.values(tree).filter(n => (
!tree[n[parentKey]]?.[childrenKey].push(n)
));
}
const tree = createTree({
data: arr,
parentKey: 'root_id',
});