I am trying to improve the time complexity and quality of the code snippet below.
I am iterating through one array to check if the element this array exists in the object, should this be true it should return the name matching the element id in the object.
how can I do this without having a nested loop?
Can someone tell me what I can do to make this algo better, please?
Thank you all in advance.
let genres = [28, 12, 878];
data = {
genres: [
{
id: 28,
name: 'Action',
},
{
id: 12,
name: 'Adventure',
},
{
id: 16,
name: 'Animation',
},
{
id: 35,
name: 'Comedy',
},
{
id: 80,
name: 'Crime',
},
{
id: 99,
name: 'Documentary',
},
{
id: 18,
name: 'Drama',
},
{
id: 10751,
name: 'Family',
},
{
id: 14,
name: 'Fantasy',
},
{
id: 36,
name: 'History',
},
{
id: 27,
name: 'Horror',
},
{
id: 10402,
name: 'Music',
},
{
id: 9648,
name: 'Mystery',
},
{
id: 10749,
name: 'Romance',
},
{
id: 878,
name: 'Science Fiction',
},
{
id: 10770,
name: 'TV Movie',
},
{
id: 53,
name: 'Thriller',
},
{
id: 10752,
name: 'War',
},
{
id: 37,
name: 'Western',
},
],
};
const getGenreName = () => {
let result = [];
for (let genre of data.genres) {
//console.log("genre", genre.name)
for (let id of genres) {
//console.log('id',genres[i])
if (id === genre.id) result.push(genre.name);
}
}
console.log(result);
};
getGenreName();
You can use reduce and includes as others have already shown. This will make the code a bit cleaner, but not change the overall runtime complexity. To improve runtime complexity you may need to use a different data structure.
For instance instead of
let genres = [1,2,3,4];
as a simple array, you could use a Set, which has a better lookup performance.
let genres = new Set([1,2,3,4]);
Then you can use this as follows
let result = data.genres
.filter(g => genres.has(g.id))
.map(g => g.name);
and won't need any explict for loops
The simplest improvement would probably be converting genres to a Set https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set
and use the has method to check if each id in the data is a member of the set of chosen genres.
You can also convert the data to a map with the ids as the keys in order to look up by id quickly instead of looping, but that is only faster if the data is reused many times.
JavaScript #reduce in the example outlined below would have O(n) time complexity. This only loops through the array once. We could use filter, and map but it would result in us having to loop through the array twice.
const getGenreName = () => {
const genreSet = new Set(genres);
return data.genres.reduce((accumulator, { id, name }) => {
if (genreSet.has(id)) accumulator.push(name);
return accumulator;
}, []);
};
console.log(getGenreName()); // [ 'Action', 'Adventure', 'Science Fiction' ]
We are initializing the reducer to start with the array [], or an empty array, and then checking to see if the genre property of the object is included in the genres array, if it isn't, return the accumulator, if it is, append it to the end of the accumulator and return it.
You wanted this in one loop, so here it is:
let result = [];
data.genres.forEach(function (e) {
if (genres.includes(e.id)) result.push(e.name);
});
console.log(result);
In case you were wondering about forEach, here's a very good reference: https://www.w3schools.com/jsref/jsref_foreach.asp
The current time complexity is O(MN) where M is the length of data.genres and N is the length of genres.
Time complexity in JavaScript depends on which engine you use, but in most cases you can use a Map to reduce this time complexity to O(max{N,M}):
const getGenreName = () => {
const dataGenresMap = new Map( // O(M)
data.genres.map(({id,...params}) => [id,params]) // O(M)
)
let result = []
for (let id of genres) { // O(N)
if (dataGenresMap.has(id)) result.push(dataGenresMap.get(id).name) // O(1)
}
console.log(result)
}
If you might be doing this more than once then I'd recommend using a Map. By creating a hash map, retrieving genre names per id is much more performant.
let genres = [28, 12, 878];
data = {
genres: [
{
id: 28,
name: 'Action',
},
{
id: 12,
name: 'Adventure',
},
{
id: 16,
name: 'Animation',
},
{
id: 35,
name: 'Comedy',
},
{
id: 80,
name: 'Crime',
},
{
id: 99,
name: 'Documentary',
},
{
id: 18,
name: 'Drama',
},
{
id: 10751,
name: 'Family',
},
{
id: 14,
name: 'Fantasy',
},
{
id: 36,
name: 'History',
},
{
id: 27,
name: 'Horror',
},
{
id: 10402,
name: 'Music',
},
{
id: 9648,
name: 'Mystery',
},
{
id: 10749,
name: 'Romance',
},
{
id: 878,
name: 'Science Fiction',
},
{
id: 10770,
name: 'TV Movie',
},
{
id: 53,
name: 'Thriller',
},
{
id: 10752,
name: 'War',
},
{
id: 37,
name: 'Western',
},
],
};
const genreById = new Map ();
data.genres.forEach(({id, name}) => genreById.set(id, name));
const pushMapValueIfTruthy = map => array => key => {
const val = map.get(key);
if (val) {
array.push(val);
}
};
/** function that takes an array, then id, and pushes corresponding name (if exists) into the array. */
const pushGenreNaneIfExists = pushMapValueIfTruthy(genreById);
const getGenreNames = (ids) => {
result = [];
ids.forEach(pushGenreNaneIfExists(result));
return result;
};
console.log(getGenreNames(genres));
This question already has answers here:
Merge two array of objects based on a key
(23 answers)
Closed 1 year ago.
This is two object. One is 'major'. Other one is 'marjorModel'.
const major = [
{
id: 1,
subject: 'math',
title: 'no good'
},
{
id: 2,
subject: 'science',
title: 'good'
}
]
const majorModel = [
{
id: 1,
amount: 23,
date: '2022-03-01'
},
{
id: 3,
amount: 26,
date: '2022-03-01'
}
]
and I want to merge new one
example)
const newObj = [
{
id: 1,
subject: 'math',
title: 'no good',
amount: 23,
date: '2022-03-01'
},
{
id: 2,
subject: 'science',
title: 'good',
amount: 26,
date: '2022-03-01'
}
]
I don't know how to merge different of other object.
please let me know!!
You can use reduce
major.reduce((acc, v, i) => {
acc.push({
...v,
...majorModel[i]
});
return acc;
}, []);
This question already has answers here:
How to sort an array of objects by date?
(4 answers)
Closed 2 years ago.
I have an array :
const arr = [
{ name: 'abc', date: '30/03/2014' },
{ name: 'cde', date: '30/03/2015' },
{ name: 'fgh', date: '20/04/2014' },
{ name: 'xyz', date: '17/09/2014' },
];
How can I sort this array so that the output would be like this:
const arr = [
{ name: 'cde', date: '30/03/2015' },
{ name: 'xyz', date: '17/09/2014' },
{ name: 'fgh', date: '20/04/2014' },
{ name: 'abc', date: '30/03/2014' },
];
// sort the array with date in latest first.
Using Array#sort with your own sorting comperator. For this split the dates and build with taht in the right sequence a new Date which can be compared.
const arr = [
{ name: 'abc', date: '30/03/2014' },
{ name: 'cde', date: '30/03/2015' },
{ name: 'fgh', date: '20/04/2014' },
{ name: 'xyz', date: '17/09/2014' },
];
arr.sort((a,b) => {
let tempA = a.date.split('/');
let tempB = b.date.split('/');
return ((new Date(tempB[2],tempB[1],tempB[0])) - (new Date(tempA[2],tempA[1],tempA[0])));
});
console.log(arr);
This question already has answers here:
JavaScript merging objects by id [duplicate]
(18 answers)
How can I merge properties of two JavaScript objects dynamically?
(69 answers)
Closed 6 years ago.
Suppose we have a java script objects on array like this:
lets say i have a variable books having collection of book
var books ={
{
id: 1,
name: 'Physics'
}
{
id: 2,
name: 'Mathematics'
}
{
id: 3,
name: 'Chemistry'
}
}
And lets say every writer have a book name inside their info like this
var writers ={
{
id: 123,
name: 'William Jeff',
book: 'Physics'
},
{
id: 123123,
name: 'John Doe',
book: 'Mathematics'
},
{
id: 1212312323,
name: 'asd Doe',
book: 'Chemistry'
},
{
id:123123,
name: 'ASD DAS',
book:'Physics'
}
}
I want to merge these javascript objects to this one :
var result = {
{
id: 1,
name: 'Physics',
writers : {
{
id: 123,
name: 'William Jeff',
},
{
id:123123,
name: 'ASD DAS',
}
},
{
id: 2,
name: 'Mathematics'
writers : {
{
id: 123123,
name: 'John Doe',
}
},
}
{
id: 3,
name: 'Chemistry',
writers : {
{
id: 1212312323,
name: 'asd Doe',
},
},
}
}
I tried using underscore foreach but it gets terribly complicated like too much loop inside loop. I think there are certainly good way to do it :)
Is there anyway?
Thanks
You can iterate both objects if they have an id:
var books = { 1: { id: 1, name: "Math" }, ...}
var writers = { 1: { id:1, name: "John Doe", book: "Math"}, ...}
Or declare them as arrays:
var books = [{ id: 1, name: "Math" }, ...]
var writers = [{ id:1, name: "John Doe", book: "Math"}, ...]
Then you can do do the for-loops
var result = {}; //or as an array
for(bookKey in books){
var book = books[bookKey];
for(writerKey in writers){
var writer = writers[writerKey];
if(writer.book == book.name){
result[bookKey] = { //for an array: result.push(object)
id: book.id,
name: book.name,
writer: {
id: writer.id,
name: write.name
}
}
}
}
}
As an object result[1] will be the book with id 1.
As an array result[0] will be the book with id "X".
At the moment I am working on an ionic app (angular1) and I am working on a huge object.
The Object will look like this:
userWorkouts: [
{
title: '3 Split',
id: 1,
workoutImg: '',
workoutSessions: {
1: {
workoutSessionName: 'Monday',
workoutExerciseList: {
1: {
exerciseName: "Pull Ups",
exerciseSets: {
1: 20,
2: 12,
3: 8
}
},
2: {
exerciseName: "Pull Ups",
exerciseSets: {
1: 20,
2: 12,
3: 8
}
}
}
}
}
},
{
title: 'Kraftausdauer Teil 1',
id: 2,
workoutImg: ''
},
{
title: 'Kraftausdauer Teil 2',
id: 3,
workoutImg: ''
},
{
title: '7 Minuten Training',
id: 4,
workoutImg: ''
},
{
title: 'Workout Zuhause',
id: 5,
workoutImg: ''
}
]
For Example: A User have x Workouts. Each Workout have x Sessions (Monday, Wednesday, Friday). A session have also x Exercises with x sets.
The Problem: I want to modify this object and I have a few problems:
Problem: I want to add Sessions (Monday, Wednesday, Friday).
var session = {
workoutSessionName: sessionName
};
userWorkouts[1].push(session)
Doesn't work, because its an object. Is there another way to "push" to an object?
if i understood try to change to this.
userWorkouts: [
{
title: '3 Split',
id: 1,
workoutImg: '',
workoutSessions: [{
{
workoutSessionName: 'Monday',
workoutExerciseList: [{
1: {
exerciseName: "Pull Ups",
exerciseSets: {
1: 20,
2: 12,
3: 8
}
},
2: {
exerciseName: "Pull Ups",
exerciseSets: {
1: 20,
2: 12,
3: 8
}
}
}
}
]
}
},
{
title: 'Kraftausdauer Teil 1',
id: 2,
workoutImg: ''
},
{
title: 'Kraftausdauer Teil 2',
id: 3,
workoutImg: ''
},
{
title: '7 Minuten Training',
id: 4,
workoutImg: ''
},
{
title: 'Workout Zuhause',
id: 5,
workoutImg: ''
}
]
and use like this
var session = {
workoutSessionName: sessionName
};
userWorkouts[1].workoutSessions.push(session)
Not sure you need to use 1. array or 2. object for workoutSessions
You could use array to hold workoutSessions instead of object because you are already using an integer as a key and then just add using push()
Check angular.extend https://docs.angularjs.org/api/ng/function/angular.extend
Here is a plunk for option 2:
https://plnkr.co/edit/x0isJdzXHq2gX7vfsQdG?p=preview