I am trying to sort this array by timestamp values. I want to sort them in ascending order and if any of them have an indefinite property, place it at the end. I currently have the error Cannot read property 'first_release_date' of undefined. How to solve this?
var array =
[
{
"id": 1969,
"cover": {
"id": 1960,
"url": "image.jpg"
},
"first_release_date": 1083542400,
"name": "Item 1"
},
{
"id": 113242,
"name": "Item 2"
},
{
"id": 25076,
"first_release_date": 1540512000,
"name": "Item 3"
},
{
"id": 1969,
"cover": {
"id": 1960,
"url": "image.jpg"
},
"name": "Item 4"
},
{
"id": 9245,
"first_release_date": 1292976000,
"name": "Item 5"
}
];
Object.keys(array).forEach((key) => {
console.log(`Before: ${array[key].name}`)
});
array.sort((a,b) => a.array.first_release_date > b.array.first_release_date);
Object.keys(array).forEach((key) => {
console.log(`After: ${array[key].name}`)
});
You are almost there. Only need to provide a default value for when there's no date. Also, sort requires you to return a number, at the moment you return a boolean, which will be cast to 0 or 1. Which will break the sort for the cases you want to return a negative number.
var array =
[
{
"id": 1969,
"cover": {
"id": 1960,
"url": "image.jpg"
},
"first_release_date": 1083542400,
"name": "Item 1"
},
{
"id": 113242,
"name": "Item 2"
},
{
"id": 25076,
"first_release_date": 1540512000,
"name": "Item 3"
},
{
"id": 1969,
"cover": {
"id": 1960,
"url": "image.jpg"
},
"name": "Item 4"
},
{
"id": 9245,
"first_release_date": 1292976000,
"name": "Item 5"
}
];
Object.values(array).forEach((val) => {
var d = new Date(val.first_release_date*1000).getFullYear();
console.log(`Before: ${ val.name} ${d }`)
});
array.sort((a,b) => ( a.first_release_date || Number.POSITIVE_INFINITY ) - ( b.first_release_date || Number.POSITIVE_INFINITY ));
Object.values(array).forEach((val) => {
var d = new Date(val.first_release_date*1000).getFullYear();
console.log(`After: ${ val.name} ${d }`)
});
var reverse = JSON.parse( JSON.stringify( array ));
reverse.sort((a,b) => ( b.first_release_date || Number.NEGATIVE_INFINITY ) - ( a.first_release_date || Number.NEGATIVE_INFINITY ));
console.log( reverse );
Related
I am trying to find a match in a object of arrays and clone this, change the ID and insert this after the found match.
Each plan has clusters and each cluster has goals, the idea is that I need to clone a goal and insert this AFTER the cloned goal (it will be loaded below this goal in the UI).
Main structure
{
"id": 100,
"title": "Plan ABC",
"clusters": [
{
"id": 1,
"subject": "Some subject",
"goals": [
{
"id": 1,
"title": "Goal A",
},
{
"id": 2,
"title": "Goal B",
},
{
"id": 3,
"title": "Goal C",
},
],
},
{
"id": 2,
"subject": "Some subject",
"goals": [
{
"id": 4,
"title": "Goal D",
},
{
"id": 5,
"title": "Goal E",
},
{
"id": 6,
"title": "Goal F",
},
],
},
]
}
My test code
// this would not work ofcourse!
const newId = 12345;
const matchToId = 2;
plan.clusters?.map(cluster => {
cluster?.goals?.map((goal, i) => {
if (goal.id === matchToId) {
// will copy goal with id 2
const copyGoal = goal;
return {...goal, ...copyGoal};
}
return {...goal};
});
// this will work but it will change the id but not copy and add a the new object
plan.clusters = clusters.map(cluster => {
return {
...cluster,
goals: cluster.goals?.filter(goal => {
if (itemId == goal.id) {
const cloned = goal;
cloned.id = 12345;
return {...goal, cloned};
}
return goal;
}),
};
});
What I want
{
"id": 100,
"title": "Plan ABC",
"clusters": [
{
"id": 1,
"subject": "Some subject",
"goals": [
{
"id": 1,
"title": "Goal A",
},
{
"id": 2,
"title": "Goal B",
},
// this will be added
{
"id": 12345,
"title": "COPIED GOAL",
},
// ---
{
"id": 3,
"title": "Goal C",
},
],
},
{
"id": 2,
"subject": "Some subject",
"goals": [
{
"id": 4,
"title": "Goal D",
},
{
"id": 5,
"title": "Goal E",
},
{
"id": 6,
"title": "Goal F",
},
],
},
]
}
This may be one possible solution to achieve the desired objective.
Code Snippet
// check if "tgtId" is present in "goals" array (parameter: "arr")
// and if so, insert "newObj" (parameter: "obj") into the array
const copyObjToGoal = (arr, tgtId, obj) => {
let resObj = {goals: arr}; // the default "result object"
const tgtIdx = arr.findIndex( // search for "tgtId"
({ id }) => id === tgtId
);
if (~tgtIdx) { // if tgtIdx is not -1 (ie, tgtId was found in arr)
const newArr = [ // non-shallow-copy of "arr" (to prevent mutation)
...arr.map(
x => ({...x})
)
]; // mutate the copied "newArr", not the parameter "arr"
newArr.splice(
tgtIdx + 1, // add "after" the "tgtId" position in "goals" array
0, // 0 indicates not to remove any element
{...obj} // destructure to shallow-copy the "newObj" object
);
resObj = { // update the resObj by re-assigning
goals: [...newArr]
};
};
return resObj; // return the result-object
};
// search and insert new/copied goal (not mutating 'myObj')
const searchAndInsert = (tgtId, newObj, obj) => (
Object.fromEntries( // transform below result back into object
Object.entries(obj) // iterate key-value pairs of "obj"
.map(([k, v]) => {
if (k !== 'clusters') { // if key is not "clusters", no change
return [k, v];
} else { // else (for "clusters"
return [ // transform the "goals" array for each cluster
k, // where "tgtId" is found
v.map( // iterate over array of "clusters"
({goals, ...r1}, idx) => {
return { // return each "clusters" array object
...r1,
...copyObjToGoal(goals, tgtId, newObj)
}
}
)
];
}
})
)
);
const myObj = {
"id": 100,
"title": "Plan ABC",
"clusters": [
{
"id": 1,
"subject": "Some subject",
"goals": [
{
"id": 1,
"title": "Goal A",
},
{
"id": 2,
"title": "Goal B",
},
{
"id": 3,
"title": "Goal C",
}
]
},
{
"id": 2,
"subject": "Some subject",
"goals": [
{
"id": 4,
"title": "Goal D",
},
{
"id": 5,
"title": "Goal E",
},
{
"id": 6,
"title": "Goal F",
}
]
}
]
};
const targetGoalId = 2;
const newGoalObj = { id: '12345', title: 'copied goal' };
console.log(
'Test case 1, from question\n',
searchAndInsert(targetGoalId, newGoalObj, myObj)
);
console.log(
'Test case 2, bespoke\n',
searchAndInsert(6, { id: '12345', title: 'copied again' }, myObj)
);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Explanation
Inline comments in the above snippet describe the significant aspects of the solution.
Notes
This solution employs below JavaScript features:
Array .findIndex()
... spread
Array .splice()
Array .map()
Object.fromEntries()
Object.entries()
De-structuring
I have an array that contains three book objects. Each book object has an array of bookIds . I want to filter by bookId (3,1) in a fast and efficient way since my array will grow up easly in the future. I tried with map, but it change my original array even with a deepCopy ! Is there a way to use the filter function instead without using recursivity ?
this.booksList =
[
{
"books": [
{
"bookId": 3
},
{
"bookId": 2
}
],
"id": 1,
"name": "Name 1",
"description": "desc 1"
},
{
"books": [
{
"bookId": 5
},
{
"bookId": 2
}
],
"id": 2,
"name": "Name 2",
"description": "desc 2"
},
{
"books": [
{
"bookId": 1
},
{
"bookId": 3
}
],
"id": 3,
"name": "Name 3",
"description": "desc 3"
}
]
The map approach :
let results = this.books.map(function (book) {
book.books = book.books.filter(x => x.bookId == 1 || x.bookId == 3);
return book;
}).filter(({ books }) => books.length);
Result with map : not expected result !
[
{
"books": [
{
"bookId": 3
}
],
"id": 1,
"name": "Name 1",
"description": "desc 1"
},
{
"books": [
{
"bookId": 1
},
{
"bookId": 3
}
],
"id": 3,
"name": "Name 3",
"description": "desc 3"
}
]
expected results :
[
{
"books": [
{
"bookId": 3
},
{
"bookId": 2
}
],
"id": 1,
"name": "Name 1",
"description": "desc 1"
},
{
"books": [
{
"bookId": 1
},
{
"bookId": 3
}
],
"id": 3,
"name": "Name 3",
"description": "desc 3"
}
]
Thanks,
I think you are looking for filter and some -
const input =
[{books:[{bookId:3},{bookId:2}],id:1,name:"Name 1",description:"desc 1"},{books:[{bookId:5},{bookId:2}],id:2,name:"Name 2",description:"desc 2"},{books:[{bookId:1},{bookId:3}],id:3,name:"Name 3",description:"desc 3"}]
const query =
[3, 1]
const result =
input.filter(({ books = [] }) =>
books.some(({ bookId = null }) => query.includes(bookId))
)
console.log(JSON.stringify(result, null, 2))
Output -
[
{
"books": [
{
"bookId": 3
},
{
"bookId": 2
}
],
"id": 1,
"name": "Name 1",
"description": "desc 1"
},
{
"books": [
{
"bookId": 1
},
{
"bookId": 3
}
],
"id": 3,
"name": "Name 3",
"description": "desc 3"
}
]
const booksList=[
{books:[{bookId:3},{bookId:2}],id:1,name:"Name 1",description:"desc 1"},
{books:[{bookId:5},{bookId:2}],id:2,name:"Name 2",description:"desc 2"},
{books:[{bookId:1},{bookId:3}],id:3,name:"Name 3",description:"desc 3"},
];
const byBookIDs = (...IDs) =>
booksList.filter(b => b.books.some(b => IDs.includes(b.bookId)));
console.log(byBookIDs(1, 3));
MDN Array Filter - to return a subset
MDN Array Some - to match (and return) as soon as some
MDN Array Includes - to find item in array
I have the following data that is an array of nested objects:
"categories": [
{
"id": 1,
"name": "Category 1",
"years": [
{ "id": 1, "name": "1" },
{ "id": 2, "name": "2" }
]
},
{
"id": 2,
"name": "Category 2",
"years": [
{ "id": 2, "name": "2" },
{ "id": 3, "name": "3" }
]
}
]
I want to extract unique years in a separate array (desired output):
[
{ "id": 1, "name": "1" },
{ "id": 2, "name": "2" },
{ "id": 3, "name": "3" },
]
When I map out the years, I'm getting an array of arrays, how should I extract the unique objects for years?
let years = categories.map( (c) => { return c.years })
You can use a Map to filter duplicate years from the array of values using id as the key, and reduce() on both categories and years using the map as the accumulator:
const categories = [{
"id": 1,
"name": "Category 1",
"years": [
{ "id": 1, "name": "1" },
{ "id": 2, "name": "2" }
]
},
{
"id": 2,
"name": "Category 2",
"years": [
{ "id": 2, "name": "2" },
{ "id": 3, "name": "3" }
]
}
];
const years = categories.reduce(
(map, category) => category.years.reduce(
(map, year) => map.set(year.id, year),
map
),
new Map()
);
console.log([...years.values()]);
You can use reduce and Map
let data = [{"id": 1,"name": "Category 1","years": [{ "id": 1, "name": "1" },{ "id": 2, "name": "2" }]},{"id": 2,"name": "Category 2","years": [{ "id": 2, "name": "2" },{ "id": 3, "name": "3" }]}]
let final = data.reduce((op,{years}) => {
years.forEach(({id, name}) => {
let key = id + '-' + name
op.set(key, op.get(key) || {id, name})
})
return op
},new Map())
console.log([...final.values()])
I am trying to learn javascript reduce and map an I came across some difficulties.
I have an array with the following format. The id of the parent is same as the location_id of the child. I need to group the array into a nested format.
arr = [
{
"id": 4583211,
"name": "Location 1",
"location_id": null,
},
{
"id": 7458894,
"name": "Location 12",
"location_id": 4583211
},
{
"id": 7463953,
"name": "Location 13",
"location_id": 4583211
},
{
"id": 80302210,
"name": "Location 121",
"location_id": 7458894
},
{
"id": 80302219,
"name": "Location 122",
"location_id": 7458894
},
{
"id": 7464314,
"name": "Location 131",
"location_id": 7463953
},
{
"id": 4583216,
"name": "Location 2",
"location_id": null,
},
{
"id": 3566353,
"name": "Location 21",
"location_id": 4583216
},
]
This array should be grouped as:
result = [
{
"id": 4583211,
"name": "Location 1",
"locations": [
{
"id": 7458894,
"name": "Location 12",
"locations": [
{
"id": 80302210,
"name": "Location 121"
},
{
"id": 80302219,
"name": "Location 122"
}
]
},
{
"id": 7463953,
"name": "Location 13",
"locations": [
{
"id": 7464314,
"name": "Location 131"
}
]
}
]
},
{
"id": 4583216,
"name": "Location 2",
"locations": [
{
"id": 3566353,
"name": "Location 21"
}
]
}
]
I tried to group it using the following method found on SO but it gives different result.
result = arr.reduce(function (r, a) {
r[a.location_id] = r[a.location_id] || [];
r[a.location_id].push(a);
return r;
}, Object.create(null));
You could do this using reduce and recursion you just need to check if parent is equal to current elements location_id.
const data = [{"id":4583211,"name":"Location 1","location_id":null},{"id":7458894,"name":"Location 12","location_id":4583211},{"id":7463953,"name":"Location 13","location_id":4583211},{"id":80302210,"name":"Location 121","location_id":7458894},{"id":80302219,"name":"Location 122","location_id":7458894},{"id":7464314,"name":"Location 131","location_id":7463953},{"id":4583216,"name":"Location 2","location_id":null},{"id":3566353,"name":"Location 21","location_id":4583216}]
function create(data, parent = null) {
return data.reduce((r, e) => {
if(parent == e.location_id) {
const o = { id: e.id, name: e.name }
const children = create(data, e.id);
if(children.length) o.locations = children;
r.push(o)
}
return r
}, [])
}
console.log(create(data))
I have an Array of Objects, each containing Array and Objects, like so:
data = [{
"id": 10022,
"date": "2017-12-31T03:44:19.963808Z",
"bought_beats": [{
"id": 10034,
"beat": {
"id": 6334,
"name": "Glass",
"producer": {
"id": 23,
"display_name": "MadReal",
}
},
"license": {
"id": 10034,
"name": "Premium",
},
}, {
"id": 894,
"beat": {
"id": 6334,
"name": "Other Name",
"producer": {
"id": 25,
"display_name": "Other Name",
}
},
"license": {
"id": 10034,
"name": "Premium",
},
}]
}, {
"moredata": "stuff"
}]
And I need to filter the bought_beats property, and only return beat, if beat.producer.id === 23
This is what I have but it's clearly not working
data.forEach(order => {
return order.bought_beats.filter(item => item.beat.id === producerId)
})
===========
Edit1:
Trying this. It "works", but it also removed some properties (id & date) from each order object (which is each index of data), so I have objects that only contain the array of "bought_beats"
var res = data.map(item => item.bought_beats.filter(item => item.beat.producer.id === 23))
========
Edit2
This seems to be 1 solution, it maintains the array and object structure the same, while it removes those unwanted elements from the bought_beats array.
data.forEach(order => {
let elementToRemoveIndex = order.bought_beats.findIndex(item => item.beat.producer.id !== 23)
order.bought_beats.splice(elementToRemoveIndex, 1)
})
Thanks #Pac0 for the continuous help
use .find over data.bought_beats since its an array,
DEMO
var data = [{
"id": 10022,
"date": "2017-12-31T03:44:19.963808Z",
"bought_beats": [{
"id": 10034,
"beat": {
"id": 6334,
"name": "Glass",
"producer": {
"id": 23,
"display_name": "MadReal",
}
},
"license": {
"id": 10034,
"name": "Premium",
},
}, {
"id": 894,
"beat": {
"id": 6334,
"name": "Other Name",
"producer": {
"id": 25,
"display_name": "Other Name",
}
},
"license": {
"id": 10034,
"name": "Premium",
},
}]
}, {
"moredata": "stuff"
}];
var result = data.find(dat => dat.bought_beats.some(item => item.beat.producer.id === 23));
console.log(result);
If I understood correctly, this should be what you want :
// project each object to its bought_beats / beats part
var beatsArrays = data.filter(x => x.bought_beats).map(x => x.bought_beats);
// flatten the array of arrays of beats into a simple array of beats
var beats = [].concat.apply([],beatsArrays).map(x => x.beat);
// filter
var relevantBeats = beats.filter(item => item.producer.id === 23);
// serve with a cherry in a sugar-frost cocktail glass (happy new year ! )
console.log(relevantBeats);
Snippet :
data = [{
"id": 10022,
"date": "2017-12-31T03:44:19.963808Z",
"bought_beats": [{
"id": 10034,
"beat": {
"id": 6334,
"name": "Glass",
"producer": {
"id": 23,
"display_name": "MadReal",
}
},
"license": {
"id": 10034,
"name": "Premium",
},
}, {
"id": 894,
"beat": {
"id": 6334,
"name": "Other Name",
"producer": {
"id": 25,
"display_name": "Other Name",
}
},
"license": {
"id": 10034,
"name": "Premium",
},
}]
}, {
"moredata": "stuff"
}];
// project each object to its bought_beats / beats part
var beatsArrays = data.filter(x => x.bought_beats).map(x => x.bought_beats);
// flatten the array of arrays of beats into a simple array of beats
var beats = [].concat.apply([],beatsArrays).map(x => x.beat);
// filter
var relevantBeats = beats.filter(item => item.producer.id === 23);
// serve with a cherry in a sugar-frost cocktail glass (happy new year ! )
console.log(relevantBeats);
// for each order
data.forEach(order => {
// we loop thorugh the bought beats array
order.bought_beats.forEach((item, index) => {
// and if there's a beat from another producer, we remove it
if (item.beat.producer.id !== producerId) order.bought_beats.splice(index, 1)
})
})