I have two arrays as types and defaultTypes. I need to display types with default values array defaultTypes.
const types = [
{
Id: 2,
Name: 'Some value here'
},
{
Id: 3,
Name: 'Some value here'
},
{
Id: 4,
Name: 'Some value here'
}
];
const defaultTypes = [
{
Id: 1,
Name: 'Default value 1'
},
{
Id: 2,
Name: 'Default value 2'
}
]
If in types does not exist some of default types (in this case Id: 1 does not exist in the types array). I need to add that object in types array.
Expected result will be:
const expectedTypes = [
{
Id: 1,
Name: '-'
},
{
Id: 2,
Name: 'Some value here'
},
{
Id: 3,
Name: 'Some value here'
},
{
Id: 4,
Name: 'Some value here'
}
];
Objects with Ids 1 and 2 always need to be in expectedTypes.
const expectedTypes = types.concat(
defaultTypes.filter(
type => !types.some(t => t.Id == type.Id)
));
explanation: basically what you want is types + stuff in defaultTypes that are not in types already.
Try this one:
let types = [{
Id: 2,
Name: 'Some value here'
},
{
Id: 3,
Name: 'Some value here'
},
{
Id: 4,
Name: 'Some value here'
}
];
const defaultTypes = [{
Id: 1,
Name: 'Default value 1'
},
{
Id: 2,
Name: 'Default value 2'
}
];
defaultTypes.forEach(dt => {
if (!types.some(t => t.Id === dt.Id)) {
types.push(dt);
}
});
types = types.sort((a, b) => a.Id - b.Id);
console.log(types);
Use this code and try
var _ = require('lodash');
defaultTypes.forEach(type => {
if (!_.find(types, { Id: type.Id })) {
types.push({ Id: type.Id, Name: '-' });
}
});
You can first use create a Set using map() which will contain its of elements in types.
Then loop through the defaultTypes and check if the Set contain the Id. If doesn't then push() it to types
At the end use sort() to order the elements by Id
const types = [ { Id: 2, Name: 'Some value here' }, { Id: 3, Name: 'Some value here' }, { Id: 4, Name: 'Some value here' } ];
const defaultTypes = [ { Id: 1, Name: 'Default value 1' }, { Id: 2, Name: 'Default value 2' } ]
let ids = new Set(types.map(x => x.Id));
defaultTypes.forEach(x => {
if(!ids.has(x.Id)) types.push(x)
})
types.sort((a,b) => a.Id - b.Id)
console.log(types)
Purpose Of Set
The purpose of Set is to make the time complexity liner O(n). If you don't use Set you will need to use some() on the types in each loop. So the time-complexity would be O(m*n)
Set.prototype.has has time complexity O(1)
You could reduce the elements in the wanted order with a Map and get all values as result.
const
types = [{ Id: 2, Name: 'Some value here' }, { Id: 3, Name: 'Some value here' }, { Id: 4, Name: 'Some value here' }],
defaultTypes = [{ Id: 1, Name: 'Default value 1' }, { Id: 2, Name: 'Default value 2' }],
result = Array.from([...defaultTypes, ...types]
.reduce((m, o) => m.set(o.Id, Object.assign({}, m.get(o.Id), o)), new Map)
.values()
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Related
I have the following arrays of objects, for example:
const data = [
{
date: '01-01',
products: [
{
id: 1,
value: 6,
label: 'Product 1'
},
{
id: 2,
value: 3,
label: 'Product 2'
}
]
},
{
date: '02-01',
products: [
{
id: 1,
value: 4,
label: 'Product 1'
},
]
},
{
date: '03-01',
products: [
{
id: 1,
value: 11,
label: 'Product 1'
},
{
id: 2,
value: 15,
label: 'Product 2'
}
]
}
]
Then I do the grouping and get the following result:
const output = [
{
id: 1,
name: 'Product 1',
data: [6, 4, 11]
},
{
id: 2,
name: 'Product 2',
data: [3, 15]
}
]
The problem with the solution is that I cannot take into account the missing value (the object with the date "02-01" does not have an object with id: 2). I need to check that the object does not exist and substitute zero instead of the missing value. Maybe you know how to do it?
Solution code below:
const result = data.map(e => e.products).flat().reduce((acc, product) => {
const index = acc.findIndex(item => item.id === product.id);
if(index === -1) {
acc.push({
id: product.id,
name: product.label,
data: [product.value]
})
return acc;
}
const findIndex = acc[index].data.findIndex((innerNode) => innerNode.id === product.id);
if (findIndex === -1) {
console.log(product.value)
acc[index].data.push(product.value);
return acc;
}
return acc;
}, []);
Expected result:
const output = [
{
id: 1,
name: 'Product 1',
data: [6, 4, 11]
},
{
id: 2,
name: 'Product 2',
data: [3, 0, 15]
}
]
You can do this in three passes:
first, you find all dates. When you first encounter a product, you will set all its values to 0 for each of those dates.
then, you iterate products and ensure that, for each date, they have a value - which will be zero by default.
finally, you format the output.
const data = [
{
date: '01-01',
products: [
{
id: 1,
value: 6,
label: 'Product 1'
},
{
id: 2,
value: 3,
label: 'Product 2'
}
]
},
{
date: '02-01',
products: [
{
id: 1,
value: 4,
label: 'Product 1'
},
]
},
{
date: '03-01',
products: [
{
id: 1,
value: 11,
label: 'Product 1'
},
{
id: 2,
value: 15,
label: 'Product 2'
}
]
}
]
// goal is to fill this for each product
let dateToValues = data.map(d => [d.date, 0]);
// build map of product-id to values-for-each-date
let products = new Map();
data.forEach(d => d.products.forEach(p => {
let values = products.get(p.id)?.data;
if (values === undefined) {
values = new Map(dateToValues); // a copy
products.set(p.id, {label: p.label, data: values});
}
values.set(d.date, p.value);
}))
// generate output, skipping dates and only showing their values
let output = [];
products.forEach((v, id) => output.push({
id: id, name: v.label, data: [... v.data.values()]}));
console.log(output)
This question already has answers here:
group array of objects by id
(8 answers)
Closed 1 year ago.
I want to group the array of objects based on the key and concat all the grouped objects into a single array. GroupBy based on the id
example,
payload
[
{
id: 1,
name: 'a'
},
{
id: 1,
name: 'b'
},
{
id: 1,
name: 'c'
},
{
id: 2,
name: 'b'
},
{
id: 2,
name: 'c'
}
]
expected response
[
[
{
id: 1,
name: 'a'
},
{
id: 1,
name: 'b'
},
{
id: 1,
name: 'c'
}
],
[
{
id: 2,
name: 'b'
},
{
id: 2,
name: 'c'
}
]
]
All the matched elements are in the same array and all the arrays should be in a single array.
Array.redue will help
const input = [
{ id: 1, name: 'a' },
{ id: 1, name: 'b' },
{ id: 1, name: 'c' },
{ id: 2, name: 'b' },
{ id: 2, name: 'c' }
];
const output = input.reduce((acc, curr) => {
const node = acc.find(item => item.find(x => x.id === curr.id));
node ? node.push(curr) : acc.push([curr]);
return acc;
}, []);
console.log(output)
Extract the ids using Set so you have a unique set of them,
then loop over those ids and filter the original array based on it.
let objects = [
{
id: 1,
name: 'a'
},
{
id: 1,
name: 'b'
},
{
id: 1,
name: 'c'
},
{
id: 2,
name: 'b'
},
{
id: 2,
name: 'c'
}
]
let ids = [...new Set(objects.map(i => i.id))]
let result = ids.map(id => objects.filter(n => id === n.id))
console.log(result)
you can create a object with ids array by using Array.reduce method, and get the object values by Object.values
var s = [{
id: 1,
name: 'a'
},
{
id: 1,
name: 'b'
},
{
id: 1,
name: 'c'
},
{
id: 2,
name: 'b'
},
{
id: 2,
name: 'c'
}
];
//go through the input array and create a object with id's, group the values to gather
var ids = s.reduce((a, c) => {
//check object has the `id` property, if not create a property and assign empty array
if (!a[c.id])
a[c.id] = [];
//push the value into desidred object property
a[c.id].push(c)
//return the accumulator
return a;
}, {});
//get the grouped array as values
var outPut = Object.values(ids);
console.log(outPut);
1) You can easily achieve the result using Map and forEach easily
const arr = [
{
id: 1,
name: "a",
},
{
id: 1,
name: "b",
},
{
id: 1,
name: "c",
},
{
id: 2,
name: "b",
},
{
id: 2,
name: "c",
},
];
const map = new Map();
arr.forEach((o) => !map.has(o.id) ? map.set(o.id, [o]) : map.get(o.id).push(o));
const result = [...map.values()];
console.log(result);
/* This is not a part of answer. It is just to give the output full height. So IGNORE IT */
.as-console-wrapper { max-height: 100% !important; top: 0; }
2) You can also achieve the result using reduce
const arr = [
{
id: 1,
name: "a",
},
{
id: 1,
name: "b",
},
{
id: 1,
name: "c",
},
{
id: 2,
name: "b",
},
{
id: 2,
name: "c",
},
];
const result = [...arr.reduce((map, curr) => {
!map.has(curr.id) ? map.set(curr.id, [curr]) : map.get(curr.id).push(curr);
return map;
}, new Map()).values()];
console.log(result);
/* This is not a part of answer. It is just to give the output full height. So IGNORE IT */
.as-console-wrapper { max-height: 100% !important; top: 0; }
Following on from my previous question, I'd like to change and extend the capability of what was suggested.
Here's the data I've got:
const things = [
{
id: 1,
title: 'Something',
categoryId: 1
},
{
id: 2,
title: 'Another thing',
categoryId: 1
},
{
id: 3,
title: 'Yet another thing',
categoryId: 2
},
{
id: 4,
title: 'One more thing',
categoryId: 4
},
{
id: 5,
title: 'Last thing',
categoryId: 4
}
]
const categories = [
{
id: 1,
title: 'Category 1'
},
{
id: 2,
title: 'Category 2'
},
{
id: 4,
title: 'Category 3'
}
]
Previously I've been shown how to do something along these lines:
const duplicatesCountWithTitle = (things, categories) => {
const thingsReduced = things.reduce((hash, { categoryId }) => {
hash[categoryId] = (hash[categoryId] || 0) + 1
return hash
}, {})
}
As you'd be able to tell, the problem with this is that it actually returns a new object, and not a new array. Also I'd like to join the categoryTitle from the categories array with the results of the duplicated count from the things array, based on the categoryId matching the id in categories.
// currently the above returns an object in the structure of:
// {
// 1: 2,
// 2: 1,
// 4: 2
// }
// what I'm after is an array like this:
// [
// { 'Category 1': 2 },
// { 'Category 2': 1 },
// { 'Category 3': 2 }
// ]
Thanks in advance, again.
Something like this?
const newArr = categories.map(category => {
const count = things.filter(thing => thing.categoryId === category.id).length;
return { [category.title]: count }
});
console.log(newArr);
https://jsfiddle.net/f3x6m12j/
You could take a Map for the relation of id and title.
const
duplicatesCountWithTitle = (things, categories) => things.reduce((hash, { categoryId }) => {
hash[categories.get(categoryId)] = (hash[categories.get(categoryId)] || 0) + 1
return hash;
}, {}),
things = [{ id: 1, title: 'Something', categoryId: 1 }, { id: 2, title: 'Another thing', categoryId: 1 }, { id: 3, title: 'Yet another thing', categoryId: 2 }, { id: 4, title: 'One more thing', categoryId: 4 }, { id: 5, title: 'Last thing', categoryId: 4 }],
categories = [{ id: 1, title: 'Category 1' }, { id: 2, title: 'Category 2' }, { id: 4, title: 'Category 3' }],
result = duplicatesCountWithTitle(
things,
categories.reduce((m, { id, title }) => m.set(id, title), new Map)
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
I have a complex array's like shown below
sectionDetail = [{id: 1, name:'ma'}, {id: 2, name:'na'}, {id: 3, name:'ra'}, {id: 4, name:'ka'}, {id: 5, name:'pa'}];
abc = [{id:'1', name:'zam', sections:['1',4]}, {id:'2', name:'dam', sections:['3']}, {id:'3', name:'nam', sections:['2','4']}];
Now I have to loop through the abc with respect to sections to replace the array elements with their respective sectionDetail values
I have tried by looping it to a new variable but my sections is getting replaced every time. below is the code i tried.
const matchingBoost = [];
const getCategoryBasedBoostList = [];
abc.forEach((item, i) => {
sectionDetail.forEach((val, index) => {
item.section.forEach((value, x) => {
if (value == val.Id) {
matchingBoost.push(val);
}
});
});
getCategoryBasedBoostList.push({
Name: item.Name,
Boost: matchingBoost
});
});
so basically I'm looking for a new array something like this
xyz = [{name:'zam', sections:[{id: 1, name:'ma'}, {id: 4, name:'ka'}]},
{name:'dam', sections:[{id: 3, name:'ra'}]}, {name:'nam', sections:[{id: 2, name:'na'}, {id: 4, name:'ka'}]}];
hoping I made sense and hoping for some response.
You can basically filter the sections from sectionDetail based on whether the object.id inside it is included in the sections of abc. I have mapped the indexes to number in both cases since one was string and the other was integer.
sectionDetail = [{id: 1, name:'ma'}, {id: 2, name:'na'}, {id: 3, name:'ra'}, {id: 4, name:'ka'}, {id: 5, name:'pa'}];
abc = [{id:'1', name:'zam', sections:['1',4]}, {id:'2', name:'dam', sections:['3']}, {id:'3', name:'nam', sections:['2','4']}];
xyz = abc.map(item => ({...item, sections: sectionDetail.filter(sect => item.sections.map(id => parseInt(id)).includes(parseInt(sect.id)))}));
console.log(xyz);
You could take a Map and then map the data with the items of sectionDetail.
var sectionDetail = [{ id: 1, name: 'ma' }, { id: 2, name: 'na' }, { id: 3, name: 'ra' }, { id: 4, name: 'ka' }, { id: 5, name: 'pa' }],
data = [{ id: '1', name: 'zam', sections: ['1', 4] }, { id: '2', name: 'dam', sections: ['3'] }, { id: '3', name: 'nam', sections: ['2', '4'] }],
map = new Map(sectionDetail.map(o => [o.id, o])),
result = data.map(({ name, sections }) =>
({ name, sections: sections.map(id => map.get(+id)) })
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
So you want to remove the id from the abc objects and replace the sections array elements with the corresponding details objects? This looks like a job for forEach and map! The code I'm about to show also does a little bit of pre-processing of the sections array to make the overall code more efficient.
const sections = sectionDetail.reduce((result, section) => {
result[section.id] = section;
return result;
}, {});
abc.forEach(item => {
delete item.id;
item.sections = item.sections.map(id => sections[id]);
});
Try like this:
const sectionDetail = [
{ id: 1, name: 'ma' },
{ id: 2, name: 'na' },
{ id: 3, name: 'ra' },
{ id: 4, name: 'ka' },
{ id: 5, name: 'pa' }];
const abc = [
{ id: '1', name: 'zam', sections: ['1', 4] },
{ id: '2', name: 'dam', sections: ['3'] },
{ id: '3', name: 'nam', sections: ['2', '4'] }
];
const desired = abc.map(({id, name, sections}) => {
return {id, name, sections : sectionDetail.filter(f => {
return sections.map(s => +s).includes(f.id)
})};
})
console.log(desired);
where +s is casting to Number type.
I have two array of objects:
var books = [
{ id: 1, name: 'Book A' },
{ id: 2, name: 'Book B' }
];
var cars = [
{ id: 1, name: 'Car A' },
{ id: 2, name: 'Car B' },
{ id: 3, name: 'Car C' },
];
I need to create an array of strings that contains:
1. The Name of the first Book in books (if there are any)
2. The Names of the first 2 Cars in cars (if there are any)
I can do:
if (books.length > 0)
var bookA = books[0].name;
or:
if (cars.length > 1) {
var carA = cars[0].name;
var carB = cars[1].name;
}
Then build the string array but I believe there might be a better way to do this.
Can use filter() and map()
var books = [
{ id: 1, name: 'Book A' },
{ id: 2, name: 'Book B' }
];
var cars = [
{ id: 1, name: 'Car A' },
{ id: 2, name: 'Car B' },
{ id: 3, name: 'Car C' }
];
var res = [books[0], cars[0], cars[1]]
.filter(e => e)// remove undefined's
.map(({name:n}) => n)
console.log(res)
If you are using ES6. You can use [...array1,...array2] to merge them. So I slice the first item in book and use map to get a new array with only string name, and map it to result array.
For the cars array I slice the first two cars and do the same
var resultArray =[];
var books = [
{ id: 1, name: 'Book A' },
{ id: 2, name: 'Book B' }
];
var cars = [
{ id: 1, name: 'Car A' },
{ id: 2, name: 'Car B' },
{ id: 3, name: 'Car C' }
];
resultArray = [...resultArray, ...books.slice(0,1).map(v => v.name)]
resultArray = [...resultArray, ...cars.slice(0,2).map(v => v.name)]
console.log(resultArray)
One of a million ways to do it. This one would allow you to easily create a data structure (arrayDefinition) that configures what property to get from which array and at which index, which you could e.g. retrieve from a RESTful webservice.
var books = [
{ id: 1, name: 'Book A' },
{ id: 2, name: 'Book B' }
];
var cars = [
{ id: 1, name: 'Car A' },
{ id: 2, name: 'Car B' },
{ id: 3, name: 'Car C' },
];
const arrayDefinition = [
{
source: 'books',
index: 0,
prop: 'name'
},
{
source: 'cars',
index: 0,
prop: 'name'
},
{
source: 'cars',
index: 1,
prop: 'name'
}
];
let resultArr = []
arrayDefinition.forEach(function(def) {
if (Array.isArray(window[def.source]) && window[def.source].length >= def.index) {
resultArr.push(window[def.source][def.index][def.prop])
}
})
console.log(resultArr)