I'm trying to expand array in JavaScript.
The object ↓
const tests = [
{
id: 1,
name: 'taro',
designs: [
{
designId: 1,
designName: "design1"
},
{
designId: 2,
designName: "design2"
}
]
},
{
id: 2,
name: 'John',
designs: [
{
designId: 3,
designName: "design3"
},
{
designId: 4,
designName: "design4"
}
]
},
{
id: 3,
name: 'Lisa',
designs: []
},
];
[
{ id: 1, name: 'taro', designId: 1, designName: 'design1' },
{ id: 1, name: 'taro', designId: 2, designName: 'design2' },
{ id: 2, name: 'John', designId: 3, designName: 'design3' },
{ id: 2, name: 'John', designId: 4, designName: 'design4' },
{ id: 3, name: 'Lisa', designId: null, designName: null },
]
It is easy to do this using double for, but I want to use it with higher-order functions.
The code I wrote
for (let i = 0; i < tests.length; i++) {
for (let j = 0; j < tests[i].designs.length; j++) {
const id = tests[i].id
const name = tests[i].name
result.push({
id,
name,
designId: tests[i].designs[j].designId,
designName: tests[i].designs[j].designName
})
}
}
In addition, it would be appreciated if you could additionally explain the difference in performance between double for and higher-order functions.
You can use .flatMap() on your tests array with an inner .map() on each designs array. The inner map on the designs array will take the properties from the currently iterated design object and merge it with the properties from the parent object. The outer .flatMap() can then be used to concatenate all returned maps into the one array:
const tests = [ { id: 1, name: 'taro', designs: [ { designId: 1, designName: "design1" }, { designId: 2, designName: "design2" } ] }, { id: 2, name: 'John', designs: [ { designId: 3, designName: "design3" }, { designId: 4, designName: "design4" } ] }, ];
const res = tests.flatMap(({designs, ...rest}) => designs.map(design => ({
...rest,
...design
})));
console.log(res);
Edit:
If you need null values to appear for your design objects if your designs array is empty, you can add the keys explicitly to a new object that you can return when the designs array is empty:
const tests = [ { id: 1, name: 'taro', designs: [] }, { id: 2, name: 'John', designs: [] }, ];
const res = tests.flatMap(({designs, ...rest}) =>
designs.length
? designs.map(design => ({
...rest,
...design
}))
: {...rest, designId: null, designName: null}
);
console.log(res);
You can use an Array.reduce function with Array.map to generate the array:
const results = tests.reduce((acc, { designs, ...rest }) => [
...acc,
...designs.map(e => ({ ...rest, ...e }))
], []);
const tests = [
{
id: 1,
name: 'taro',
designs: [
{
designId: 1,
designName: "design1"
},
{
designId: 2,
designName: "design2"
}
]
},
{
id: 2,
name: 'John',
designs: [
{
designId: 3,
designName: "design3"
},
{
designId: 4,
designName: "design4"
}
]
},
];
const results = tests.reduce((acc, { designs, ...rest }) => [
...acc,
...designs.map(e => ({ ...rest, ...e }))
], []);
console.log(results);
You can use the higher-order function Array.prototype.reduce() with Array.prototype.map()
const newArr = tests.reduce((prev, {designs, ...current}) => [
...prev, ...designs.map(design => ({...design,...current}));
]
, []);
The performance in your approach and this higher-order approach is the same because Array.prototype.reduce runs through the whole array and just facilitates the initialValue approach for us.
Related
const groups = [
{
name: "1",
subjects: [1, 2]
},
{
name: "2",
subjects: [1]
},
]
const subjects = [
{
id: 1,
name: "English",
},
{
id: 2,
name: "Mathematics",
},
{
id: 3,
name: "Physics",
},
]
example:[
{
name: "1",
subjects: [
{
id: 1,
name: "English",
},
{
id: 2,
name: "Mathematics",
},
]
const groupsSubject = groups.map(group => {
return {
...group,
subjects: subjects.id
}
})
This is my answer:
const groups = [
{
name: '1',
subjects: [ 1, 2 ]
},
{
name: '2',
subjects: [ 1 ]
}
]
const subjects = [
{
id: 1,
name: 'English'
},
{
id: 2,
name: 'Mathematics'
},
{
id: 3,
name: 'Physics'
}
]
const group = (groups, subjects) => {
return groups.map((group) => ({
name: group.name,
subjects: subjects.filter((subject) => group.subjects.includes(subject.id))
}))
}
console.log(group(groups, subjects))
Output:
[
{ name: '1', subjects: [ [Object], [Object] ] },
{ name: '2', subjects: [ [Object] ] }
]
You can create an object lookup of subjects based on the id. Then iterate through your group and assign the respective subjects.
const groups = [ { name: "1", subjects: [1, 2] }, { name: "2", subjects: [1] }],
subjects = [ { id: 1, name: "English", }, { id: 2, name: "Mathematics", }, { id: 3, name: "Physics", }, ],
lookup = Object.fromEntries(subjects.map(o => [o.id, o])),
result = groups.map(({name, subjects}) => ({name, subjects: subjects.map(id => ({...lookup[id]}))}));
console.log(result);
.as-console-wrapper { min-height: 100%!important; top: 0; }
Using Map and Array#map, create a map where the subject id is the key and the subject is the value
Using Array#map, iterate over the groups array. In each iteration, the subjects list will be created using Array#map and Map#get to transform ids to objects
const _getGroupWithSubjectDetails = (groups = [], subjects = []) => {
const subjectMap = new Map(
subjects.map(subject => ([subject.id, subject]))
);
return groups.map(({ subjects = [], ...group }) => ({
...group,
subjects: subjects.map(subjectId => ({ ...(subjectMap.get(subjectId) || {}) }))
}));
}
const
groups = [ { name: "1", subjects: [1, 2] }, { name: "2", subjects: [1] } ],
subjects = [ { id: 1, name: "English" }, { id: 2, name: "Mathematics" }, { id: 3, name: "Physics" } ];
console.log( _getGroupWithSubjectDetails(groups, subjects) );
I have an object with a structure like below
const data = [
{ academicYearId: 1, classLevelId: 1, subjectId: 1, ...},
{ academicYearId: 1, classLevelId: 1, subjectId: 2, ...},
{ academicYearId: 1, classLevelId: 1, subjectId: 3, ...},
,,,
]
I need to create a function that will return unique columns e.g
const uniqueColumns = ( val, columns)=> {
//
}
const val = [
{ id: 1, name: 'n1', val: 1 },
{ id: 1, name: 'n1', val: 2 },
{ id: 2, name: 'n2', val: 1 },
{ id: 3, name: 'n2', val: 2 }
]
let result = uniqueColumns(val)
console.log(val)
/**
* Expected
* [{ id: 1, name: 'n1'}, { id: 2, name: 'n2'}, { id: 3, name: 'n2'}]
*/
}
I have tried to look at the various answers in the post How to get distinct values from an array of objects in JavaScript? and I have managed to come up with the below
const uniqueColumns = (val, columns) =>
([...new Set(
val.map(item =>
columns.reduce((prev, next) =>
({[next]: item[next], ...prev}), {})
).map(item => JSON.stringify(item)))
].map(item => JSON.parse(item)))
const val = [
{ id: 1, name: 'n1', val: 1 },
{ id: 1, name: 'n1', val: 2 },
{ id: 2, name: 'n2', val: 1 },
{ id: 3, name: 'n2', val: 2 }
]
const result = uniqueColumns(val, ['id', 'name'])
console.log(result)
What I was inquiring is if there is a better approach instead of having to Convert Object to string and back to object to achieve this
You can use array reduce method.
const val = [
{ id: 1, name: "n1", val: 1 },
{ id: 1, name: "n1", val: 2 },
{ id: 2, name: "n2", val: 1 },
{ id: 3, name: "n2", val: 2 },
];
const uniqueColumns = (val, columns) => {
let ret = val.reduce((p, c) => {
let obj = {};
columns.forEach((x) => (obj[x] = c[x]));
let key = Object.values(obj);
if (!p[key]) p[key] = obj;
return p;
}, {});
return Object.values(ret);
};
const result = uniqueColumns(val, ["id", "name"]);
console.log(result);
I have a structure in which the number of arrangements can vary:
array1 = [
{local: {id: 1, name: 'local1'}},
{local: {id: 2, name: 'local2'}},
{local: {id: 3, name: 'local3'}},
{local: {id: 4, name: 'local4'}},
{local: {id: 5, name: 'local5'}}
];
array2 = [
{local: {id: 1, name: 'local1'}},
{local: {id: 3, name: 'local3'}},
{local: {id: 3, name: 'local4'}},
{local: {id: 3, name: 'local5'}},
];
array3 = [
{local: {id: 1, name: 'local1'}},
{local: {id: 3, name: 'local2'}},
{local: {id: 3, name: 'local3'}},
{local: {id: 3, name: 'local5'}},
];
I need to create a new array from these, in which this new array is ordered first by the ids that are repeated in all the arrays and then the ones that are not repeated, should be something like this:
newArray = [
{local: {id: 1, name: 'local1'}},
{local: {id: 3, name: 'local3'}},
{local: {id: 5, name: 'local5'}},
{local: {id: 2, name: 'local2'}},
{local: {id: 4, name: 'local4'}}
]
Someone who can help me please!!
Converting all the arrays to objects for fast searching.
const array1 = [{
local: {
id: 1,
name: 'local1'
}
},
{
local: {
id: 2,
name: 'local2'
}
},
{
local: {
id: 3,
name: 'local3'
}
},
{
local: {
id: 4,
name: 'local4'
}
},
{
local: {
id: 5,
name: 'local5'
}
}
];
const array2 = [{
local: {
id: 1,
name: 'local1'
}
},
{
local: {
id: 3,
name: 'local3'
}
},
{
local: {
id: 3,
name: 'local4'
}
},
{
local: {
id: 3,
name: 'local5'
}
},
];
const array3 = [{
local: {
id: 1,
name: 'local1'
}
},
{
local: {
id: 3,
name: 'local2'
}
},
{
local: {
id: 3,
name: 'local3'
}
},
{
local: {
id: 3,
name: 'local5'
}
},
];
const obj1 = array1.reduce((acc, item) => {
acc[item.local.id] = item;
return acc;
}, {});
const obj2 = array2.reduce((acc, item) => {
acc[item.local.id] = item;
return acc;
}, {});
const obj3 = array3.reduce((acc, item) => {
acc[item.local.id] = item;
return acc;
}, {});
const result = {
...obj3,
...obj2,
...obj1
};
const output = [];
const temp = [];
for (let key in result) {
if (obj1[key] && obj2[key] && obj3[key]) {
output.push(result[key]);
} else temp.push(result[key]);
}
console.log([...output, ...temp]);
I would do it like this (may not be the optimum solution):
/* Same Arrays as yours */ const array1=[{local:{id:1,name:"local1"}},{local:{id:2,name:"local2"}},{local:{id:3,name:"local3"}},{local:{id:4,name:"local4"}},{local:{id:5,name:"local5"}}],array2=[{local:{id:1,name:"local1"}},{local:{id:3,name:"local3"}},{local:{id:3,name:"local4"}},{local:{id:3,name:"local5"}}],array3=[{local:{id:1,name:"local1"}},{local:{id:3,name:"local2"}},{local:{id:3,name:"local3"}},{local:{id:3,name:"local5"}}];
function myFunc(arrays) {
// All items, with duplicates
const allItems = [].concat.apply([], arrays);
// All IDs, without duplicates thanks to `Set`
const allIDs = Array.from(
allItems.reduce((set, item) => set.add(item.local.id), new Set())
);
// Helper function used for sorting
const isInAllArrays = id => arrays.every(
arr => arr.some(item => item.local.id === id)
);
// Sort the IDs based on whether they are in all arrays or not
allIDs.sort((a, b) => {
const _a = isInAllArrays(a), _b = isInAllArrays(b);
if (_a !== _b) return _a ? -1 : 1;
return 0;
});
// Map all IDs to the first element with this ID
return allIDs.map(id => allItems.find(item => item.local.id === id));
}
const newArray = myFunc([array1, array2, array3]);
// Just for readability in the demo below
console.log(JSON.stringify(newArray).split('},{').join('},\n{'));
1) Traverse all arrays and build an object with keys as id and value include object and also maintain the frequency of occurrence (count).
2) Now, Object.values of above object and sort them based on 'count'.
You will get most frequent items at top.
const sort = (...arrs) => {
const all = {};
arrs
.flat()
.forEach(
(obj) =>
(all[obj.local.id] =
obj.local.id in all
? { ...all[obj.local.id], count: all[obj.local.id].count + 1 }
: { ...obj, count: 1 })
);
return Object.values(all)
.sort((a, b) => b.count - a.count)
.map(({ count, ...rest }) => rest);
};
array1 = [
{ local: { id: 1, name: "local1" } },
{ local: { id: 2, name: "local2" } },
{ local: { id: 3, name: "local3" } },
{ local: { id: 4, name: "local4" } },
{ local: { id: 5, name: "local5" } },
];
array2 = [
{ local: { id: 1, name: "local1" } },
{ local: { id: 3, name: "local3" } },
{ local: { id: 3, name: "local4" } },
{ local: { id: 3, name: "local5" } },
];
array3 = [
{ local: { id: 1, name: "local1" } },
{ local: { id: 3, name: "local2" } },
{ local: { id: 3, name: "local3" } },
{ local: { id: 3, name: "local5" } },
];
console.log(sort(array1, array2, array3))
I have 2 arrays:
0: {id: 2, name: "TMA"}
1: {id: 3, name: "Hibbernate"}
0: {id: 1, name: "FB.DE"}
1: {id: 2, name: "TMA"}
2: {id: 3, name: "Hibbernate"}
3: {id: 4, name: "Event.it A"}
4: {id: 5, name: "Projket 2"}
5: {id: 6, name: "Projekt 1"}
I want to compare them and delete the objects with the id 2 and 3 cause both arrays have them and thats the similarity.
This is my Code so far:
const projectListOutput = projectsOfPersonArray.filter(project => data.includes(project));
console.log(projectListOutput);
But every time i run this projectListOutput is empty.
When using includes dont compare objects, Just build data as array of strings. Remaining code is similar to what you have.
arr1 = [
{ id: 2, name: "TMA" },
{ id: 3, name: "Hibbernate" },
];
arr2 = [
{ id: 1, name: "FB.DE" },
{ id: 2, name: "TMA" },
{ id: 3, name: "Hibbernate" },
{ id: 4, name: "Event.it A" },
{ id: 5, name: "Projket 2" },
{ id: 6, name: "Projekt 1" },
];
const data = arr1.map(({ id }) => id);
const result = arr2.filter(({ id }) => !data.includes(id));
console.log(result);
Your data array probably does not contain the exact same object references than projectsOfPersonArray. Look at the code below:
[{ foo: 'bar' }].includes({ foo: 'bar' });
// false
Objects look equal, but they don't share the same reference (= they're not the same).
It's safer to use includes with primitive values like numbers or strings. You can for example check the ids of your objects instead of the full objects.
You compare different objects, so every object is unique.
For filtering, you need to compare all properties or use a JSON string, if the order of properties is equal.
var exclude = [{ id: 2, name: "TMA" }, { id: 3, name: "Hibbernate" }],
data = [{ id: 2, name: "TMA" }, { id: 3, name: "Hibbernate" }, { id: 1, name: "FB.DE" }, { id: 2, name: "TMA" }, { id: 3, name: "Hibbernate" }, { id: 4, name: "Event.it A" }, { id: 5, name: "Projket 2" }, { id: 6, name: "Projekt 1" }],
result = data.filter(project =>
!exclude.some(item => JSON.stringify(item) === JSON.stringify(project))
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can do something similar to the next:
const source = [{
id: 1,
name: "FB.DE"
},
{
id: 2,
name: "TMA"
},
{
id: 3,
name: "Hibbernate"
},
{
id: 4,
name: "Event.it A"
},
{
id: 5,
name: "Projket 2"
},
{
id: 6,
name: "Projekt 1"
}
]
const toRemove = [{
id: 2,
name: "TMA"
},
{
id: 3,
name: "Hibbernate"
}
]
/**create object where keys is object "id" prop, and value is true**/
const toRemoveMap = toRemove.reduce((result, item) => ({
...result,
[item.id]: true
}), {})
const result = source.filter(item => !toRemoveMap[item.id])
You can make function from it:
function removeArrayDuplicates (sourceArray, duplicatesArray, accessor) {
const toRemoveMap = duplicatesArray.reduce((result, item) => ({
...result,
[item[accessor]]: true
}), {});
return sourceArray.filter(item => !toRemoveMap[item[accessor]])
}
removeArrayDuplicates(source, toRemove, 'id')
Or even better, you can make it work with a function instead of just property accessor:
function removeDuplicates (sourceArray, duplicatesArray, accessor) {
let objectSerializer = obj => obj[accessor];
if(typeof accessor === 'function') {
objectSerializer = accessor;
}
const toRemoveMap = duplicatesArray.reduce((result, item) => ({
...result,
[objectSerializer(item)]: true
}), {});
return sourceArray.filter(item => !toRemoveMap[objectSerializer(item)])
}
removeDuplicates(source, toRemove, (obj) => JSON.stringify(obj))
This function will help you merge two sorted arrays
var arr1 = [
{ id: 2, name: 'TMA' },
{ id: 3, name: 'Hibbernate' },
]
var arr2 = [
{ id: 1, name: 'FB.DE' },
{ id: 2, name: 'TMA' },
{ id: 3, name: 'Hibbernate' },
{ id: 4, name: 'Event.it A' },
{ id: 5, name: 'Projket 2' },
]
function mergeArray(array1, array2) {
var result = []
var firstArrayLen = array1.length
var secondArrayLen = array2.length
var i = 0 // index for first array
var j = 0 // index for second array
while (i < firstArrayLen || j < secondArrayLen) {
if (i === firstArrayLen) { // first array doesn't have any other members
while (j < secondArrayLen) { // we copy rest members of first array as a result
result.push(array2[j])
j++
}
} else if (j === secondArrayLen) { // second array doesn't have any other members
while (i < firstArrayLen) { // we copy the rest members of the first array to the result array
result.push(array1[i])
i++
}
} else if (array1[i].id < array2[j].id) {
result.push(array1[i])
i++
} else if (array1[i].id > array2[j].id) {
result.push(array2[j])
j++
} else {
result.push(array1[i])
i++
j++
}
}
return result
}
console.log(mergeArray(arr1,arr2));
I need to delete entire object that do not have passed
here is the array
const array = [{
course: 1,
list: [{
id: 1,
name: "john",
code: true
},
{
id: 1,
name: "maria",
code: true
},
]
},
{
course: 2,
list: [{
id: 3,
name: "rose"
},
{
id: 4,
name: "mark",
code: true
}
]
}
]
That i need is remove obj that not have code:true, and get this
const array = [{
course: 1,
list: [{
id: 1,
name: "john",
code: true
}, ]
},
{
course: 2,
list: [{
id: 1,
name: "mark",
code: true
}]
}
]
I tried to make a map inside a filter, but it does not work at all
const remove = array.filter(function(lines) {
return lines.map(line => line.list.map(list => list.code))
});
You can map through the array, then copy all properties of the specific item and separately do the filtering on the list attribute.
const array = [{
course: 1,
list: [{
id: 1,
name: "john",
code: true
},
{
id: 1,
name: "maria",
code: true
},
]
},
{
course: 2,
list: [{
id: 3,
name: "rose"
},
{
id: 4,
name: "mark",
code: true
}
]
}
]
const filter = arr => arr.map(arrItem => ({
...arrItem,
list: arrItem.list.filter( listItem => listItem.code )
})
)
console.log( filter(array) )
const filtered = [];
arr.forEach(item => {
const list = item.list.filter(listItem => listItem.code);
if(list.length > 0) {
filter.push({ ...item, list });
}
});
This approach will only add items to the filtered output array if the list contains any items after filtering out those with code: false. To include them anyway, you could do:
const filtered = arr.map(item => ({
...item,
list: item.list.filter(listItem => listItem.code)
});