[{id:1, name:'Chan', supervisor:''},
{id:2, name:'Wong', supervisor:'1'},
{id:3, name:'Fong', supervisor:'1'},
{id:4, name:'Ho', supervisor:'2'},
]
expected result
[
{
id: 1,
name: "Chan",
supervisor: "",
children: [
{
id: 2,
name: "Wong",
supervisor: "1",
children: [{ id: 4, name: "Ho", supervisor: "2" }]
},
{ id: 3, name: "Fong", supervisor: "1" }
]
}
]
I want to achieve this format like the above. Have tried to use lodash map and filter.
Want to know if there any fastest method to do that? Many thanks in advance.
Currently I have tried.
let children = [];
const transformedDataRecords = records.map((record) => {
let user = _.filter(records, { id: record.supervisor });
if (user.length>0) {
console.log(user[0].children);
if(!!(user[0].children)){
children = user[0].children;
}
children.push(record);
user = { ...user, children };
console.log(user);
}
});
Starting from
const staff = [{id:1, name:'Chan', supervisor:''},
{id:2, name:'Wong', supervisor:'1'},
{id:3, name:'Fong', supervisor:'1'},
{id:4, name:'Ho', supervisor:'2'},
]
This is a nice place to use Maps. You can keep a reference to each object by its ID without having to care about its location in the array:
const staffById = new Map(
// key-value pairs
staff.map(person => [person.id, person])
)
// Create the children arrays
staff.forEach(
person => {
if (person.supervisor !== "") {
// Maps do care about whether data is String or Number so we have to
// convert the supervisor field to Number to ensure they match.
const supervisorParsed = Number(person.supervisor)
const supervisorObj = staffById.get(supervisorParsed)
// Ensure there is an array to insert into
supervisorObj.children = supervisorObj.children || []
supervisorObj.children.push(person)
}
}
)
// References updated, now filter out everyone from the top-level array who is not a top-level supervisor.
const output = staff.filter(person => person.supervisor === '')
Related
I have two array of objects in which if property grp from arrobj1 is
same as SERVICE and ISACTIVE is true from arrobj2, then return array of object using
javascript
Tried
let result = arrobj1.filter(e=>
arrobj2.some(i=> i.ISACTIVE===true && e.grp === i.SERVICE);
);
var arrobj1=[
{
id:"SetupFS",
grp:"fs",
title: "xxx"
},
{
id:"ExtendFS",
grp:"fs",
title: "yyy"
},
{
id:"RebootServer",
grp:"os",
title: "yyy"
},
]
var arrobj2=[
{id:1, ISACTIVE:true, TASK:'SetupFS', SERVICE: "fs" },
{id:2, ISACTIVE:false, TASK:'RebootServer', SERVICE:"os" },
{id:3, ISACTIVE:false, TASK:'ExtendFS', SERVICE: "fs" },
]
Expected Result
[
{
id:"SetupFS",
grp:"fs",
title: "xxx"
}
]
You don't need filter for second item, you need to check if the item with corresponding index in arrobj1 with grp value equal to SERVICE value in arrobj2
var arrobj1=[
{
id:"SetupFS",
grp:"fs",
title: "xxx"
},
{
id:"ExtendFS",
grp:"fs",
title: "yyy"
},
{
id:"RebootServer",
grp:"os",
title: "yyy"
},
]
var arrobj2=[
{id:1, ISACTIVE:true, TASK:'SetupFS', SERVICE: "fs" },
{id:2, ISACTIVE:false, TASK:'RebootServer', SERVICE:"os" },
{id:3, ISACTIVE:false, TASK:'ExtendFS', SERVICE: "fs" },
]
let result = arrobj2.filter((item, i) =>
item.SERVICE === arrobj1[i].grp
);
console.log(result)
A simpler method
// gets two results wit the equals
let filteredList = [];
for (const item of arrobj1) {
// include TASK === item.id to get the expected answer
const inArray = arrobj2.find(e => e.ISACTIVE && e.TASK === item.id && e.SERVICE === item.grp);
if (inArray) {
filteredList.push(item)
}
}
console.log(filteredList)
with filters in the question it returns two items
e => e.ISACTIVE && e.SERVICE === item.grp
0: Object
id: "SetupFS"
grp: "fs"
title: "xxx"
1: Object
id: "ExtendFS"
grp: "fs"
title: "yyy"
Hope it helps
but if this is not what was expected, I'll delete the answer
This 2 arrays have multiple objects that has the the same ID but different dates
const names= [
{id:'1',name:'a',date:'1604616214'},
{id:'1',name:'Angel',date:'1604616215'},
{id:'2',name:'b',date:'2004616214'},
{id:'2',name:'Karen',date:'2004616215'},
{id:'3',name:'a',date:'3004616220'},
{id:'3',name:'Erik',date:'3004616221'}
]
const lastnames= [
{id:'1',lastname:'a',date:'4004616220'},
{id:'1',lastname:'Ferguson',date:'4004616221'},
{id:'2',lastname:'b',date:'5004616220'},
{id:'2',lastname:'Nixon',date:'5004616221'},
{id:'3',lastname:'a',date:'6004616222'},
{id:'3',lastname:'Richard',date:'6004616223'}
]
The data is in moment().unix() to create a number "easy to compare"
I want to create a Third array that merge the 2 arrays and create objects with the same id and the last updated date object.
The output should be something like this
const third = [
{id:'1',name:'Angel',lastname:'Ferguson'},
{id:'2',name:'Karen',lastname:'Nixon'},
{id:'3',name:'Erik',lastname:'Richard'}
]
This is what i got so far, if i updated the arrays it duplicates and i need to have only the last updated object
const third = names.map(t1 => ({...t1, ...lastnames.find(t2 => t2.id === t1.id)}))
I'm going to assume since you have the spread operator and Array.find in your example that you can use ES6, which includes for of and Object.values as you see below.
An object and simple looping is used to reduce the amount of times you're iterating. In your example, for every element in names you're iterating over last names to find one with the same ID. Not only is that not ideal for performance, but it doesn't work because every time you're finding the same element with that ID (the first one with that ID in the array).
const names = [
{ id: "1", name: "a", date: "1604616214" },
{ id: "1", name: "Angel", date: "1604616215" },
{ id: "2", name: "b", date: "2004616214" },
{ id: "2", name: "Karen", date: "2004616215" },
{ id: "3", name: "a", date: "3004616220" },
{ id: "3", name: "Erik", date: "3004616221" },
];
const lastnames = [
{ id: "1", lastname: "a", date: "4004616220" },
{ id: "1", lastname: "Ferguson", date: "4004616221" },
{ id: "2", lastname: "b", date: "5004616220" },
{ id: "2", lastname: "Nixon", date: "5004616221" },
{ id: "3", lastname: "a", date: "6004616222" },
{ id: "3", lastname: "Richard", date: "6004616223" },
];
const profiles = {};
function addToProfiles(arr, profiles) {
for (let obj of arr) {
if (obj.id != null) {
// Inits to an empty object if it's not in the profiles objects
const profile = profiles[obj.id] || {};
profiles[obj.id] = { ...profile, ...obj };
}
}
}
addToProfiles(names, profiles);
addToProfiles(lastnames, profiles);
const third = Object.values(profiles);
The idea is to group the objects by their ids, then merge each group according to the rules, maximizing date for each type of record (name and lastname)
// the input data
const names= [
{id:'1',name:'a',date:'1604616214'},
{id:'1',name:'Angel',date:'1604616215'},
{id:'2',name:'b',date:'2004616214'},
{id:'2',name:'Karen',date:'2004616215'},
{id:'3',name:'a',date:'3004616220'},
{id:'3',name:'Erik',date:'3004616221'}
]
const lastnames= [
{id:'1',lastname:'a',date:'4004616220'},
{id:'1',lastname:'Ferguson',date:'4004616221'},
{id:'2',lastname:'b',date:'5004616220'},
{id:'2',lastname:'Nixon',date:'5004616221'},
{id:'3',lastname:'a',date:'6004616222'},
{id:'3',lastname:'Richard',date:'6004616223'}
]
// make one long array
let allNames = [...names, ...lastnames]
// a simple version of lodash _.groupBy, return an object like this:
// { '1': [ { objects with id==1 }, '2': [ ... and so on ] }
function groupById(array) {
return array.reduce((acc, obj) => {
let id = obj.id
acc[id] = acc[id] || [];
acc[id].push(obj);
return acc;
}, {});
}
// this takes an array of objects and merges according to the OP rule
// pick the maximum date name object and maximum date lastname object
// this sorts and searches twice, which is fine for small groups
function mergeGroup(id, group) {
let sorted = group.slice().sort((a, b) => +a.date < +b.date)
let name = sorted.find(a => a.name).name
let lastname = sorted.find(a => a.lastname).lastname
return {
id,
name,
lastname
}
}
// first group, then merge
let grouped = groupById(allNames)
let ids = Object.keys(grouped)
let results = ids.map(id => {
return mergeGroup(id, grouped[id])
})
console.log(results)
I tried to come up with a solution using filter functions. End result contains the format you wanted. check it out.
const names= [
{id:'1',name:'a',date:'1604616214'},
{id:'1',name:'Angel',date:'1604616215'},
{id:'2',name:'b',date:'2004616214'},
{id:'2',name:'Karen',date:'2004616215'},
{id:'3',name:'a',date:'3004616220'},
{id:'3',name:'Erik',date:'3004616221'}
]
const lastnames= [
{id:'1',lastname:'a',date:'4004616220'},
{id:'1',lastname:'Ferguson',date:'4004616221'},
{id:'2',lastname:'b',date:'5004616220'},
{id:'2',lastname:'Nixon',date:'5004616221'},
{id:'3',lastname:'a',date:'6004616222'},
{id:'3',lastname:'Richard',date:'6004616223'}
]
// filter out last updated objects from both arrays
var lastUpdatednames = names.filter(filterLastUpdate,names);
console.log(lastUpdatednames);
var lastUpdatedsurnames = lastnames.filter(filterLastUpdate,lastnames);
console.log(lastUpdatedsurnames);
// combine the properties of objects from both arrays within filter function.
const third = lastUpdatednames.filter(Combine,lastUpdatedsurnames);
console.log(third);
function filterLastUpdate(arrayElement)
{
var max = this.filter( i => arrayElement.id==i.id ).reduce(
function(prev, current)
{
return (prev.date > current.date) ? prev : current
}
)
return max.date == arrayElement.date ;
}
function Combine(firstArray)
{
var subList= this.filter( i => firstArray.id==i.id );
//console.log(subList);
//console.log(subList[0]);
if (subList)
{
firstArray.lastname = subList[0].lastname;
return true;
}
return false ;
}
Here is last output:
[…]
0: {…}
date: "1604616215"
id: "1"
lastname: "Ferguson"
name: "Angel"
1: {…}
date: "2004616215"
id: "2"
lastname: "Nixon"
name: "Karen"
2: {…}
date: "3004616221"
id: "3"
lastname: "Richard"
name: "Erik"
I have a collection based on interface below
interface ITurbine {
id: string;
name: string;
turbine: ITurbine[];
}
collection can have very deep values with same structure.
How can I extract all children in one list with one level
pseudo exampleTurbines
[
{id:1, name:test1, turbine:null},
{id:2, name:test2, turbine:[
id:3, name:test3, turbine: {id:4, name:test4, turbine:null}
id:5, name:test5, turbine: {id:6, name:test6, turbine:null}
]},
]
expected result is to extract all turbines in one array
[
{id:1, name:test1},
{id:2, name:test2},
..
{id:6, name:test6}
]
const getInLineTurbineArray = (turbines: ITurbine[]): ITurbine[] => {
let inlineTurbines: ITurbine[] = [];
inlineTurbines.forEach((turbine) => {
var test = extractTurbines(turbine);
inlineTurbines.push(test)
});
return inlineTurbines;
};
const extractTurbines = (turbines: ITurbine) => {
if (turbines.turbine) {
turbines.turbine.forEach((child) => {
return extractTurbines(child);
});
} else {
return turbines;
}
};
What you re trying to accomplish is called "flattening" and specifically flattening a Tree-like structure where a Node( a Turbine) can have 0..n leafs(other turbines). You can try to use recursion to work with structures like that.
const tree = [
{id:1, name:"test1", turbine:null},
{id:2, name:"test2", turbine:[
{id:3, name:"test3", turbine: {id:4, name:"test4", turbine:null}},
{id:5, name:"test5", turbine: {id:6, name:"test6", turbine:null}}
]
},
]
function flattenTree(tree) {
let result = [];
if(Array.isArray(tree)) {
for(const node of tree) {
result.push(
{id: node.id, name: node.name}
);
if(node.turbine) {
result = result.concat(flattenTree(node.turbine));
}
}
} else {
result.push({id: tree.id, name: tree.name})
}
return result;
}
console.log(flattenTree(tree))
while trying to come up with a solution I also saw that the example object you gave looks more like this interface:
interface ITurbine {
name: string;
id: number;
turbine?: ITurbine | ITurbine[];
}
I don't know if this was a typo on your end but the solution should probably work either way.
I have an array of players objects like this :
var players = [{
id: "thisIsID1",
name: "William",
otherProps
},
{
id: "thisIsID2",
name: "Shakespeare",
otherProps
},
{
id: "thisIsID3",
name: "Lola",
otherProps
}]
And I have and array of their ID that has been shuffled, like so :
var shuffledIDs = ["thisIsID2", "thisIsID3", "thisIsID1"]
How can I sort the players var so the objects are in the same order as the corresponding IDs of shuffledIDs ?
Edit: different names just for the sake of making players different
If your data is short, then you can sort it with the following one-liner:
players = shuffledIDs.map(id => players.find(v => v.id == id))
Essentially, for every id in shuffledID, it finds the element in players with that id and puts it in the correct spot. However, this takes O(n^2) time so it might not scale well for larger data. If you want a faster method, you can maintain an object of IDs:
var ids = {};
players.forEach(v => ids[v.id] = v);
players = shuffledIDs.map(v => ids[v]);
You can achieve it using array .find() method:
var players = [{
id: "thisIsID1",
name: "William"
},
{
id: "thisIsID2",
name: "Shakespeare"
},
{
id: "thisIsID3",
name: "Lola"
}]
var shuffledIDs = ["thisIsID2", "thisIsID3", "thisIsID1"]
var result = shuffledIDs.map(x => players.find(p=>p.id === x))
console.log(result)
Create object with keys and values as index from shuffle array.
Use sort method and prioritize bases above shuffled indexes. This way should even the case of duplicate data in players.
var players = [
{
id: "thisIsID1",
name: "William"
},
{
id: "thisIsID2",
name: "Shakespeare"
},
{
id: "thisIsID3",
name: "Lola"
}
];
const shuffleIds = ["thisIsID2", "thisIsID3", "thisIsID1"];
const shuf_idx = Object.fromEntries(shuffleIds.map((x, i) => [x, i]));
players.sort((a, b) => shuf_idx[a.id] - shuf_idx[b.id]);
console.log(players);
With .map() and .find() where using index of the element:
const shuffledIDs = ["thisIsID2", "thisIsID3", "thisIsID1"];
const players = [{ id: "thisIsID1", name: "William" }, { id: "thisIsID2", name: "Shakespeare" }, { id: "thisIsID3", name: "Lola" }];
const result = players.map((e, i) => players.find(f => f.id === shuffledIDs[i]));
console.log(result);
when a value of a property of the object is null or contains "missing" the whole object should be filterd.
this works for filtering null
object = (object.filter(o => !Object.values(o).includes(null))
But how can I add 2 filters and how to implement a filter that filters strings that contain the word "missing"
object = (object.filter(o => !Object.values(o).includes(null) | ("missing")));
object:
[
{ id: 'blockstack-iou',
name: 'Blockstack (IOU)',
image: 'missing_large.png'
}
{ id: 'qtum',
name: 'Qtum',
image:
'https://assets.coingecko.com/coins/images/684/large/qtum.png?1547034438',
price_change_percentage: -53.2869774915231
}
]
Use Array.prototype.every().
Use && to combine multiple tests.
let object = [{
id: 'blockstack-iou',
name: 'Blockstack (IOU)',
image: 'missing_large.png'
},
{
id: 'qtum',
name: 'Qtum',
image: 'https://assets.coingecko.com/coins/images/684/large/qtum.png?1547034438',
price_change_percentage: -53.2869774915231
}
];
console.log(object.filter(o => Object.values(o).every(prop =>
prop != null && !prop.toString().includes("missing"))));
Use && and match
const object = [
{id: 'blockstack-iou',
name: 'Blockstack (IOU)',
image: 'missing_large.png'
},
{ id: 'qtum',
name: 'Qtum',
image:
'https://assets.coingecko.com/coins/images/684/large/qtum.png?1547034438',
price_change_percentage: -53.2869774915231
}
]
const filtered = (object.filter(o =>!Object.values(o).includes(null) && !Object.values(o).toString().match(/missing/gi)));
console.log(filtered)
If you're looking for a solution that is easily extensible, consider storing all your "conditions" as an array and running through them using .every().
const object = [
{str: "this_is_missing"},
{str: null},
{str: "valid"}
];
const validations = [
i => i !== null, //Value isn't null
i => !i.toString().includes("missing") //Value doesn't contain "missing"
];
const validate = (val) => validations.every(fn => fn(val));
const result = object.filter(obj => Object.values(obj).every(validate));
console.log(result);