Given the following json array
const groups=[{ id:1, parent:null, groupName:'Others', maxScore:3},
{id:2, parent:null, groupName: 'Group 1', maxScore:0},
{id:3, parent:2, groupName:'Others, maxScore:2},
{id:4, parent:2, groupName:'Sub Group 1', maxScore:1}];
What would be a more performance oriented approach to sum maxScores within parents ?
It looks like a tree structure, but nodes don't have references to their children.
Im trying a map.reduce approach right now
function addMaxScoreToGroups(groups) {
return groups.map((group) => {
const newGroup = group;
if (group.parent === null && group.name !== 'Others') {
const children = groups.filter(elem => elem.parent === group.id);
if (children) {
newGroup.maxScore = children.map(x => x.maxScore)
.reduce((value, acum) => value + acum);
}
}
return newGroup;
});
}
Expected result would be
const groups=[{ id:1, parent:null, groupName:'Others', maxScore:3},
{id:2, parent:null, groupName: 'Group 1', maxScore:0,maxPosibleScore:3},
{id:3, parent:2, groupName:'Others, maxScore:2},
{id:4, parent:2, groupName:'Sub Group 1', maxScore:1}];
Iterate the with Array.reduce(). For each object create a new entry (or update existing one in case of parent) by cloning the object using object spread (or Object.assign().
If the object has a parent, create the parent if it doesn't exist, and add/update the maxPossibleScore.
Convert back to array using Object.values().
const groups=[{ id:1, parent:null, groupName:'Others', maxScore:3}, {id:3, parent:2, groupName:'Others', maxScore:2}, {id:4, parent:2, groupName:'Sub Group 1', maxScore:1}, {id:2, parent:null, groupName: 'Group 1', maxScore:5}];
const ms = 'maxScore';
const mps = 'maxPossibleScore';
const result = Object.values(groups.reduce((r, o) => {
// clone
const c = { ...o };
// if current parent initalized before, update maxPossibleScore
if(!o.parent && r[o.id]) c[mps] = r[o.id][mps] + c[ms];
r[o.id] = c;
if(o.parent) {
// if parent doesn't exist init
r[o.parent] = r[o.parent] || {};
// if parent.maxPossibleScore doesn't exist init it
if(!(mps in r[o.parent])) {
r[o.parent][mps] = r[o.parent][ms] || 0;
}
r[o.parent][mps] += o[ms];
}
return r;
}, {}));
console.log(result);
Related
[{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 === '')
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.
Hello there good Samaritan, i would like to use Lodash and find the user with most books in the array.
const books = [ {id:0, name: 'Adam', title: 'xx'}, {id:1, name:'Jack', title:'yy'}, { id: 2, name: 'Adam',title:'zz' } ]
Thanks in advance :)
function search_book(nameKey, myArray){
for (var i=0; i < myArray.length; i++) {
if (myArray[i].book === nameKey) {
return myArray[i];
}
}
}
var array = [
{ book:"deep learning", value:"this", other: "that" },
{ book:"ml", value:"this", other: "that" }
];
var resultObject = search_book("ml", array);
console.log(resultObject)
_.filter(list, { name: 'Adam' })
var group = _.countBy(list, function(item){return item.name});
That will get the counts of author in the list, then you can sort and find the one with the largest.
You can generate a function with lodash's _.flow():
Count the objects by the name property
Convert the resulting object of the previous step to [key, value] pairs,
Find the pair with the max value
Get the key (the name)
const { flow, countBy, toPairs, maxBy, tail, head } = _
const fn = flow(
arr => countBy(arr, 'name'), // count by name
toPairs, // convert to [key, value] pairs
arr => maxBy(arr, tail), // find the max by the value (tail)
head // get the key (head)
)
const books = [ {id:0, name: 'Adam', title: 'xx'}, {id:1, name:'Jack', title:'yy'}, { id: 2, name: 'Adam',title:'zz' } ]
const result = fn(books)
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.js"></script>
And the same idea using Lodash/fp:
const { flow, countBy, toPairs, maxBy, tail, head } = _
const fn = flow(
countBy('name'), // count by name
toPairs, // convert to [key, value] pairs
maxBy(tail), // find the max by the value (tail)
head // get the key (head)
)
const books = [ {id:0, name: 'Adam', title: 'xx'}, {id:1, name:'Jack', title:'yy'}, { id: 2, name: 'Adam',title:'zz' } ]
const result = fn(books)
console.log(result)
<script src='https://cdn.jsdelivr.net/g/lodash#4(lodash.min.js+lodash.fp.min.js)'></script>
I have an array of objects like this:
let list = [
{
'items': [
'item 1',
'item 2'
]
},
{
'items': [
'item 3'
]
}
]
and I want flatten the arrays from this object like this:
['item 1','item 2','item 3']
Which JavaScript function should I use for this output?
I have tried with the map function:
list.map(i => i.items)
but I got this output:
[["item 1","item 2"],["item 3"]]
NOTE : I am looking for any existing function or an answer that is wrapped in a function so I can just call a function - not write the loop out myself.
You can flat the map() result using Array.prototype.flatMap():
The flatMap() method first maps each element using a mapping function, then flattens the result into a new array.
let list = [
{
'items': [
'item 1',
'item 2'
]
},
{
'items': [
'item 3'
]
}
]
list = list.flatMap(i => i.items);
console.log(list);
You could use reduce(). There is no way to avoid looping since Array prototype methods do loops internally
let list = [
{
'items': [
'item 1',
'item 2'
]
},
{
'items': [
'item 3'
]
}
];
const res = list.reduce((a,c) => [...a, ...c.items],[])
console.log(res)
There are multiple ways you can achieve this:
const list = [{'items': ['item 1','item 2']},{'items': ['item 3']}]
// Using map and flat
console.log(list.map(o => o.items).flat())
// Using flatMap
console.log(list.flatMap(o => o.items))
// Using reduce
console.log(list.reduce((a, o) => a.concat(o.items), []))
// Using a plain old for loop (wrapped in a function)
const getItems = list => {
let temp = []
for (let i = 0; i < list.length; i++) {
const items = list[i].items
for (let j = 0; j < items.length; j++) {
temp.push(items[j])
}
}
return temp
}
console.log(getItems(list))
However, if you want a performance-first solution, reduce + for loop is the way to go:
const list = [{'items': ['item 1','item 2']},{'items': ['item 3']}]
console.log(list.reduce((a, o) => {
for (var i = 0; i < o.items.length; i++) a.push(o.items[i])
return a
}, []))
Check this jsperf for test cases.
you can use the Array.reduce method and follow the docs here
{
let list = [
{
'items': [
'item 1',
'item 2'
]
},
{
'items': [
'item 3'
]
}
];
/**
* #parameter {Array} arg
*/
function splat (arg) {
return arg.reduce((total, { items }) => [...total, ...items], []);
}
console.log(splat(list));
}
You could also play with recursive function to make a more generic function that accepts array of nested objects.
https://lodash.com/docs/4.17.14#flatten
And
https://lodash.com/docs/4.17.14#flattenDeep
Look like they're exactly what you need.
I have a json array like this:
[
{id:1, another_id:1},
{id:2, another_id:1},
{id:3, another_id:2}
]
Is it possible to divide this into json arrays based on the key another_id. In this case two json arrays should be created like this
jsonArr1 = [
{id:1, another_id:1},
{id:2, another_id:1}
]
jsonArr2 = [
{id:3, another_id:2}
]
another_id will be varying. Please help guys
If you do not know how many different result arrays you will have, you should not try to make a variable for each of them. Instead put them in an object, where each object property corresponds to a single possible value of another_id, and the value for it is the corresponding array.
You can achieve that with reduce:
var data = [{id:1, another_id:1},{id:2, another_id:1},{id:3, another_id:2}];
var result = data.reduce( (acc, obj) => {
acc[obj.another_id] = acc[obj.another_id] || [];
acc[obj.another_id].push(obj);
return acc;
}, {});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
If you want different variables then you can build a function which will return filtered array based on the passed value. This will use Array.filter()
function formatData(check) {
var data = [{
id: 1,
another_id: 1
},
{
id: 2,
another_id: 1
},
{
id: 3,
another_id: 2
}
];
return data.filter(el => el.another_id === check);
}
jsonArr1 = formatData(1);
jsonArr2 = formatData(2);
console.log(jsonArr1);
console.log(jsonArr2);
I hope the code below will work for you. As this will create two separate json arrays Arr1 for id and Arr2 for another_id
data = [
{id:1, another_id:1},
{id:2, another_id:1},
{id:3, another_id:2}
];
console.log(data);
var Arr1 = [];
var Arr2 = [];
for(var i in data){
Arr1.push(data[i].id);
Arr2.push(data[i].another_id);
}