Javascript re-order array of object by value - javascript

How do I re-order array of object showing below by follow value. If follow value is not -1, move the item below to the item that has the id value same as follow value.
Here is the example.
let charObj = [
{ id: 8, name: 'Catelyn Stark', follow: -1 },
{ id: 7, name: 'Jaime Lannister', follow: 8 },
{ id: 3, name: 'Jon Snow', follow: -1 },
{ id: 4, name: 'Daenerys Targaryen', follow: 7 },
{ id: 5, name: 'Sansa Stark', follow: 4 }
];
Expected output will be;
let charObj = [
{ id: 8, name: 'Catelyn Stark', follow: -1 },
{ id: 7, name: 'Jaime Lannister', follow: 8 },
{ id: 4, name: 'Daenerys Targaryen', follow: 7 },
{ id: 5, name: 'Sansa Stark', follow: 4 },
{ id: 3, name: 'Jon Snow', follow: -1 }
];
Not sure if I can use sort(). What is the best way to re-order this object?

I think this will do what you're asking. I'm sure it could be made more efficient, but unless your list gets quite large that shouldn't make much practical difference. Also, this assumes any character will only have one follower. If that's not the rule, then the function will have to be adjusted.
let charObj = [
{ id: 8, name: "Catelyn Stark", follow: -1 },
{ id: 7, name: "Jaime Lannister", follow: 8 },
{ id: 3, name: "Jon Snow", follow: -1 },
{ id: 4, name: "Daenerys Targaryen", follow: 7 },
{ id: 5, name: "Sansa Stark", follow: 4 }
];
function sortChars(chars) {
let result = [];
let leaders = chars.filter(c => c.follow === -1);
for (let i = 0; i < leaders.length; i++) {
let current = leaders[i];
while (current) {
result.push(current);
let next = charObj.find(c => c.follow === current.id);
current = next;
}
}
return result;
}
console.log(sortChars(charObj));

Related

Group array of objects by multiple nested values

I have an array of objects which presents tasks. These tasks are categorized (primary / secondary category).
let tasks = [
{
id: 1,
name: 'Cleanup desk',
primary_category: {
id: 1,
name: 'Indoor'
},
secondary_category: {
id: 2,
name: 'Surfaces'
}
},
{
id: 2,
name: 'Cleanup office floors',
primary_category: {
id: 1,
name: 'Indoor'
},
secondary_category: {
id: 3,
name: 'Ground'
}
},
{
id: 3,
name: 'Water plants',
primary_category: {
id: 2,
name: 'Outdoor'
},
secondary_category: {
id: 3,
name: 'Irrigation'
}
}
];
I now try to create a categories accordion in my frontend and therefore need to group my array differently. The structure should look like:
1) primary category
> secondary category
> tasks
> secondary category
> tasks
2) primary category
> secondary category
> tasks
Therefore I'm trying to achieve a structure similar to this:
let tasks_categorized = [
{
id: 1,
name: 'Indoor',
secondary_categories: [
{
id: 2,
name: 'Surfaces',
tasks: [
{
id: 1,
name: 'Cleanup desk'
}
]
},
{
id: 3,
name: 'Ground',
tasks: [
{
id: 2,
name: 'Cleanup office floors'
}
]
}
]
},
{
id: 2,
name: 'Outdoor',
secondary_categories: [
{
id: 3,
name: 'Irrigation',
tasks: [
{
id: 3,
name: 'Water plants'
}
]
}
]
}
];
I tried using groupBy by lodash but this does not allow grouping by multiple nested key-value pairs. Does anybody know an approach to solve this?
Thank you in advance!
The following provided approach is going to achieve the expected result within a single reduce cycle without any further nested loops.
It does so by implementing a reducer function which creates and/or aggregates at time a prioritized category task while iterating another task array. But most importantly it keeps track of a task item's related primary and secondary categories via a Map based lookup. This lookup reference together with a result array are properties of this function's return value which has to be partly provided as the reduce method's initial value as follows ... { result: [] }.
function createAndAggregatePrioritizedCategoryTask(
{ lookup = new Map, result }, item
) {
const { primary_category, secondary_category, ...taskRest } = item;
const { id: primaryId, name: primaryName } = primary_category;
const { id: secondaryId, name: secondaryName } = secondary_category;
const primaryKey = [primaryId, primaryName].join('###');
const secondaryKey = [primaryKey, secondaryId, secondaryName].join('###');
let primaryCategory = lookup.get(primaryKey);
if (!primaryCategory) {
// create new primary category item.
primaryCategory = {
id: primaryId,
name: primaryName,
secondary_categories: [],
};
// store newly created primary category reference in `lookup`.
lookup.set(primaryKey, primaryCategory);
// push newly created primary category reference to `result`.
result.push(primaryCategory);
}
let secondaryCategory = lookup.get(secondaryKey);
if (!secondaryCategory) {
// create new secondary category item.
secondaryCategory = {
id: secondaryId,
name: secondaryName,
tasks: [],
};
// store newly created secondary category reference in `lookup`.
lookup.set(secondaryKey, secondaryCategory);
// push newly created secondary category reference into the
// `secondary_categories` array of its related primary category.
primaryCategory
.secondary_categories
.push(secondaryCategory);
}
// push the currently processed task-item's rest-data as
// item into the related secondary category's `task` array.
secondaryCategory
.tasks
.push(taskRest);
return { lookup, result };
}
let tasks = [{
id: 1,
name: 'Cleanup desk',
primary_category: { id: 1, name: 'Indoor' },
secondary_category: { id: 2, name: 'Surfaces' },
}, {
id: 2,
name: 'Cleanup office floors',
primary_category: { id: 1, name: 'Indoor' },
secondary_category: { id: 3, name: 'Ground' },
}, {
id: 3,
name: 'Water plants',
primary_category: { id: 2, name: 'Outdoor' },
secondary_category: { id: 3, name: 'Irrigation' },
}];
const { result: tasks_categorized } = tasks
.reduce(createAndAggregatePrioritizedCategoryTask, { result: [] });
console.log({ tasks_categorized });
.as-console-wrapper { min-height: 100%!important; top: 0; }
You could take a dynamic approach with an array of arrays with functions and keys for the nested arrays.
const
tasks = [{ id: 1, name: 'Cleanup desk', primary_category: { id: 1, name: 'Indoor' }, secondary_category: { id: 2, name: 'Surfaces' } }, { id: 2, name: 'Cleanup office floors', primary_category: { id: 1, name: 'Indoor' }, secondary_category: { id: 3, name: 'Ground' } }, { id: 3, name: 'Water plants', primary_category: { id: 2, name: 'Outdoor' }, secondary_category: { id: 3, name: 'Irrigation' } }],
groups = [
[o => o, 'primary category'],
[o => o.primary_category, 'secondary category'],
[o => o.secondary_category, 'tasks']
],
result = tasks.reduce((r, o) => {
groups.reduce((parent, [fn, children]) => {
const { id, name } = fn(o);
let item = (parent[children] ??= []).find(q => q.id === id)
if (!item) parent[children].push(item = { id, name });
return item;
}, r);
return r;
}, {})[groups[0][1]];
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Remove duplicate elements of an array, in an object of arrays, dynamically

I have checked other solutions but none fit the criterion of my problem
This solution does not have the ability to dynamically check each node
Problem summarized
I wish to create an algorithm that is able to check an object that has nodes of different data types, for duplicated objects in nodes that are specifically of the datatype array.
I have the following dataset:
task = {
content: "lorem....",
customer: [
{ id: 1, name: "hello" },
{ id: 2, name: "sup" },
],
end: "2020-08-13 10:09:48",
project: [{ id: 1 }, { id: 1 }, { id: 2 }],
vendor: [{ id: 2 }, { id: 2 }, { id: 3 }],
};
I wish to be able to dynamically check which of the objects (or nodes? and the algo has to recognize that it is an array) has duplicates, and reduce them to be in this form:
task = {
content: "lorem....",
customer: [
{ id: 1, name: "hello" },
{ id: 2, name: "sup" },
],
end: "2020-08-13 10:09:48",
project: [{ id: 1 }, { id: 2 }],
vendor: [{ id: 2 }, { id: 3 }],
};
EDIT
The algorithm needs to be able to handle a dynamic number of nodes (example 1), however , the duplicates will only happen 1 level down (Thanks for pointing out).
example 1 (there is 1 less node here ) :
task = {
content: "lorem....",
customer: [
{ id: 1, name: "hello" },
{ id: 2, name: "sup" },
],
end: "2020-08-13 10:09:48",
project: [{ id: 1 }, { id: 2 }],
};
Here is my proposed solution to remove duplicate elements from any array in the task object:
const uniq = array => {
const map = {};
const result = [];
for (let i = 0; i < array.length; i++) {
// since elements can be objects, need to do a deep comparison.
const element = JSON.stringify(array[i]);
if (map[element] === undefined) {
map[element] = true;
result.push(array[i]);
}
}
return result;
}
const task = {
content: "lorem....",
customer: [
{ id: 1, name: "hello" },
{ id: 2, name: "sup" },
],
end: "2020-08-13 10:09:48",
project: [{ id: 1 }, { id: 1 }, { id: 2 }],
vendor: [{ id: 2 }, { id: 2 }, { id: 3 }],
};
for (const key in task) {
if (Array.isArray(task[key])) {
task[key] = uniq(task[key])
}
}
console.log('deduped:', task);

check the difference between two arrays of objects in javascript [duplicate]

This question already has answers here:
How to get the difference between two arrays of objects in JavaScript
(22 answers)
Closed 1 year ago.
I need some help. How can I get the array of the difference on this scenario:
var b1 = [
{ id: 0, name: 'john' },
{ id: 1, name: 'mary' },
{ id: 2, name: 'pablo' },
{ id: 3, name: 'escobar' }
];
var b2 = [
{ id: 0, name: 'john' },
{ id: 1, name: 'mary' }
];
I want the array of difference:
// [{ id: 2, name: 'pablo' }, { id: 3, name: 'escobar' }]
How is the most optimized approach?
I´m trying to filter a reduced array.. something on this line:
var Bfiltered = b1.filter(function (x) {
return x.name !== b2.reduce(function (acc, document, index) {
return (document.name === x.name) ? document.name : false
},0)
});
console.log("Bfiltered", Bfiltered);
// returns { id: 0, name: 'john' }, { id: 2, name: 'pablo' }, { id: 3, name: 'escobar' } ]
Thanks,
Robot
.Filter() and .some() functions will do the trick
var b1 = [
{ id: 0, name: 'john' },
{ id: 1, name: 'mary' },
{ id: 2, name: 'pablo' },
{ id: 3, name: 'escobar' }
];
var b2 = [
{ id: 0, name: 'john' },
{ id: 1, name: 'mary' }
];
var res = b1.filter(item1 =>
!b2.some(item2 => (item2.id === item1.id && item2.name === item1.name)))
console.log(res);
You can use filter to filter/loop thru the array and some to check if id exist on array 2
var b1 = [{ id: 0, name: 'john' }, { id: 1, name: 'mary' }, { id: 2, name: 'pablo' }, { id: 3, name: 'escobar' } ];
var b2 = [{ id: 0, name: 'john' }, { id: 1, name: 'mary' }];
var result = b1.filter(o => !b2.some(v => v.id === o.id));
console.log(result);
Above example will work if array 1 is longer. If you dont know which one is longer you can use sort to arrange the array and use reduce and filter.
var b1 = [{ id: 0, name: 'john' }, { id: 1, name: 'mary' }, { id: 2, name: 'pablo' }, { id: 3, name: 'escobar' } ];
var b2 = [{ id: 0, name: 'john' }, { id: 1, name: 'mary' }];
var result = [b1, b2].sort((a,b)=> b.length - a.length)
.reduce((a,b)=>a.filter(o => !b.some(v => v.id === o.id)));
console.log(result);
Another possibility is to use a Map, allowing you to bring down the time complexity to O(max(n,m)) if dealing with a Map-result is fine for you:
function findArrayDifferences(arr1, arr2) {
const map = new Map();
const maxLength = Math.max(arr1.length, arr2.length);
for (let i = 0; i < maxLength; i++) {
if (i < arr1.length) {
const entry = arr1[i];
if (map.has(entry.id)) {
map.delete(entry.id);
} else {
map.set(entry.id, entry);
}
}
if (i < arr2.length) {
const entry = arr2[i];
if (map.has(entry.id)) {
map.delete(entry.id);
} else {
map.set(entry.id, entry);
}
}
}
return map;
}
const arr1 = [{id:0,name:'john'},{id:1,name:'mary'},{id:2,name:'pablo'},{id:3,name:'escobar'}];
const arr2 = [{id:0,name:'john'},{id:1,name:'mary'},{id:99,name:'someone else'}];
const resultAsArray = [...findArrayDifferences(arr1,arr2).values()];
console.log(resultAsArray);

Reorder array of objects based on attribute

I have an array of objects, each with an 'id' and a 'name'. I'm retrieving an 'id' from the server and need to reorder the array starting from this id.
Example code:
var myList = [
{
id: 0,
name: 'Joe'
},
{
id: 1,
name: 'Sally'
},
{
id: 2,
name: 'Chris'
},
{
id: 3,
name: 'Tiffany'
},
{
id: 4,
name: 'Kerry'
}
];
Given an 'id' of 2, how can I reorder the array so my output is as follows:
var newList = [
{
id: 2,
name: 'Chris'
},
{
id: 3,
name: 'Tiffany'
},
{
id: 4,
name: 'Kerry'
},
{
id: 0,
name: 'Joe'
},
{
id: 1,
name: 'Sally'
}
];
Try this:
function orderList(list, id){
return list.slice(id).concat(list.slice(0,id));
}
Link to demo
You could slice the array at given index and return a new array using spread syntax.
const myList = [{id:0,name:'Joe'},{id:1,name:'Sally'},{id:2,name:'Chris'},{id:3,name:'Tiffany'},{id:4,name:'Kerry'}];
const slice = (arr, num) => [...arr.slice(num), ...arr.slice(0, num)];
console.log(slice(myList, 2));
myList.sort(function(a,b){
return a.id>2===b.id>2?a.id-b.id:b.id-a.id;
});
newList=myList;
http://jsbin.com/kenobunali/edit?console
You could splice the wanted part and use splice to insert it at the end of the array.
var myList = [{ id: 0, name: 'Joe' }, { id: 1, name: 'Sally' }, { id: 2, name: 'Chris' }, { id: 3, name: 'Tiffany' }, { id: 4, name: 'Kerry' }],
id = 2;
myList.splice(myList.length, 0, myList.splice(0, myList.findIndex(o => o.id === id)));
console.log(myList);
using es6 spread syntax
var myList = [{ id: 0, name: 'Joe' }, { id: 1, name: 'Sally' }, { id: 2, name: 'Chris' }, { id: 3, name: 'Tiffany' }, { id: 4, name: 'Kerry' }],
id = 2;
var index = myList.findIndex(o => o.id == id);
var arr = myList.splice(0, index);
var result = [...myList, ...arr];
console.log(result);

How union arrays with validate only one property

Joining of Arrays.
I'm in need of running a "Join Array" objects, but, I need duplicated objects to be removed, see:
Example
var objArray1 = [
{ Id: 1, Name: 'João', Order: 2 },
{ Id: 2, Name: 'Pedro', Order: 5 }
];
var objArray2 = [
{ Id: 2, Name: 'Pedro', Order: 6 },
{ Id: 3, Name: 'Manoel', Order: 9 }
];
Actual code:
var result = _.union(objArray1,objArray2);
=> [
{ Id: 1, Name: 'João', Order: 2 },
{ Id: 2, Name: 'Pedro', Order: 5 },
{ Id: 2, Name: 'Pedro', Order: 6 },
{ Id: 3, Name: 'Manoel', Order: 9 }
];
I need this result:
[
{ Id: 1, Name: 'João', Order: 2 },
{ Id: 2, Name: 'Pedro', Order: 5 },
{ Id: 3, Name: 'Manoel', Order: 9 }
];
Basic I need join arrays with filter the one property, I need is possible with For but I would like a better solution
use underscore unique function as follows
var result = _.uniq(_.union(objArray1, objArray2), false, function(item){ return item.Id; });
not 100% sure if the false should be true
or, as seems to be a trend on SO - the sexy ES2015 version
var result = _.uniq(_.union(objArray1, objArray2), false, item => item.Id);

Categories

Resources