Need to merge objects with the same keys but different values - javascript

I want to find the shortest and most beautiful way to convert an array of objects with the same values of the category key:
[{
"label": "Apple",
"category": "Fruits"
}, {
"label": "Orange",
"category": "Fruits"
}, {
"label": "Potato",
"category": "Vegetables"
}, {
"label": "Tomato",
"category": "Vegetables"
}, {
"label": "Cherry",
"category": "Berries"
}]
to the one with grouped labels from the same category:
[{
"label": ["Apple", "Orange"],
"category": "Fruits"
}, {
"label": ["Potato", "Tomato"],
"category": "Vegetables"
}, {
"label": ["Cherry"],
"category": "Berries"
}]

You could use an object as hash table and group the categories.
var data = [{ "label": "Apple", "category": "Fruits" }, { "label": "Orange", "category": "Fruits" }, { "label": "Potato", "category": "Vegetables" }, { "label": "Tomato", "category": "Vegetables" }, { "label": "Cherry", "category": "Berries" }],
grouped = [];
data.forEach(function (a) {
if (!this[a.category]) {
this[a.category] = { label: [], category: a.category };
grouped.push(this[a.category]);
}
this[a.category].label.push(a.label);
}, Object.create(null));
console.log(grouped);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Here's how I would do this using lodash:
_(coll)
.groupBy('category')
.map((v, k) => ({
category: k,
label: _.map(v, 'label')
}))
.value()
Basically, groupBy() creates an object with unique categories as keys. Then, map() turns this object back into an array, where each item has the structure you need.

Here is my attempt to solve your problem
var result = [];
var input = [{
"label": "Apple",
"category": "Fruits"
}, {
"label": "Orange",
"category": "Fruits"
}, {
"label": "Potato",
"category": "Vegetables"
}, {
"label": "Tomato",
"category": "Vegetables"
}, {
"label": "Cherry",
"category": "Berries"
}];
var cat = [];
for(i = 0; i < input.length; i++) {
if(!cat[input[i].category]) {
cat[input[i].category] = {category: input[i].category, label:[input[i].label]};
result.push(cat[input[i].category]);
} else {
cat[input[i].category].label.push(input[i].label);
}
}
console.log(result);

Related

Creating an object which contains unique item from a nested array

I have an array of objects called employees. I need a solution that will return me a list of groups and respective employees present in the group along with the group properties.
The example is below, I have used an object but the result can also be an array that has a property called groupName within an object. [{groupName:"developer", employees:[],...}..] As long as the response returns a list of groups with their corresponding employees.
Below is the solution I did but I need a solution with a better time complexity that is O(n).
const employees = [
{ "name": "John Doe",
"id": "1",
"groups": [
{ "id": "developerId", "name": "developer", "color": "#fff" },
{ "id": "engineerId", "name": "engineer", "color": "#fff" }
],
"groupId":["developerId", "engineerId"]
},
{ "name": "Jane Doe",
"id": "2",
"groups": [
{ "id": "developerId", "name": "developer", "color": "#fff" },
{ "id": "testerId", "name": "tester", "color": "#fff" }
],
"groupId":["developerId", "testerId"]
}
]
//Solution O(m*n)
let groups = {};
employees.forEach((item) => {
item.groups.forEach((group) => {
if (!groups[group.name]) {
groups[group.name] = {
employees: [item.id],
...group,
};
} else {
groups[group.name].employees = [...groups[group.name].employees, item.id];
}
});
});
//result
{
"developer":{
"id":"developerId",
"employee":[
"1",
"2"
],
"color":"#fff"
},
"engineer":{
"id":"employeeId",
"employee":[
"1",
],
"color":"#fff"
},
"tester":{
"id":"testerId",
"employee":[
"2",
],
"color":"#fff"
}
}
Using Array#reduce and Array#forEach:
const employees = [
{
"name": "John Doe",
"id": "1",
"groups": [
{ "id": "developerId", "name": "developer", "color": "#fff" },
{ "id": "engineerId", "name": "engineer", "color": "#fff" }
],
"groupId": ["developerId", "engineerId"]
},
{
"name": "Jane Doe",
"id": "2",
"groups": [
{ "id": "developerId", "name": "developer", "color": "#fff" },
{ "id": "testerId", "name": "tester", "color": "#fff" }
],
"groupId": ["developerId", "testerId"]
}
];
const groups = employees.reduce((acc, { id: employeeId, groups = [] }) => {
groups.forEach(({ id, name, color }) => {
acc[name] = {
id, color, employee: [...(acc[name]?.employee ?? []), employeeId]
};
});
return acc;
}, {});
console.log(groups);
If you like to add some speed, you could use the old fashioned for statement for iterating, especially of having only a single result object.
This approach does not create an object again and again and uses the already existing objects.
const
employees = [{ name: "John Doe", id: "1", groups: [{ id: "developerId", name: "developer", color: "#fff" }, { id: "engineerId", name: "engineer", color: "#fff" }], groupId: ["developerId", "engineerId"] }, { name: "Jane Doe", id: "2", groups: [{ id: "developerId", name: "developer", color: "#fff" }, { id: "testerId", name: "tester", color: "#fff" }], groupId: ["developerId", "testerId"] }],
result = {};
for (const { id: employeeId, groups } of employees) {
for (const { id, name, color } of groups) {
result[name] ??= { id, color, employee: [] };
result[name].employee.push(employeeId);
}
}
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

structuring obj array based on their attributes [duplicate]

This question already has answers here:
How can I group an array of objects by key?
(32 answers)
Closed 1 year ago.
can you help me with this problem with js\react?
I'm trying to manage 2 arrays due to obtain a new object based on their shared attribute (Array A: "id" and Array B: "parent")
I think isn't hard but I'm struggling to do it atm :(
Array A
[{
"id": "606f1a2bebb5fb53804dd3d5",
"name": "cc",
}, {
"id": "606f1a30cfe84430c41dce88",
"name": "bb",
}, {
"id": "606f1a4ed2ff554e4ea11b82",
"name": "ff",
}]
Array B
[{
"id": "3344",
"color": "pink",
"parent": "606f1a2bebb5fb53804dd3d5",
}, {
"id": "3453",
"color": "blue",
"parent": "606f1a30cfe84430c41dce88",
}, {
"id": "3331",
"color": "yellow",
"parent": "606f1a4ed2ff554e4ea11b82",
}, {
"id": "4442",
"color": "black",
"parent": "606f1a30cfe84430c41dce88",
}]
I want merge these two arrays and create a new one where the array B objects are split by "id" of array A.
Something like this:
[{
"606f1a2bebb5fb53804dd3d5": [{
"id": "3344",
"color": "pink",
"parent": "606f1a2bebb5fb53804dd3d5",
}]
}, {
"606f1a30cfe84430c41dce88": [{
"id": "3453",
"color": "blue",
"parent": "606f1a30cfe84430c41dce88",
}, {
"id": "4442",
"color": "black",
"parent": "606f1a30cfe84430c41dce88",
}]
}, {
"606f1a4ed2ff554e4ea11b82": [{
"id": "3331",
"color": "yellow",
"parent": "606f1a4ed2ff554e4ea11b82",
}]
}]
Thanks very much guys
You just need array b for grouping and another object for keeping track of the max key inside of a group.
const
data = [{ id: "3344", color: "pink", parent: "606f1a2bebb5fb53804dd3d5" }, { id: "3453", color: "blue", parent: "606f1a30cfe84430c41dce88" }, { id: "3331", color: "yellow", parent: "606f1a4ed2ff554e4ea11b82" }, { id: "4442", color: "black", parent: "606f1a30cfe84430c41dce88" }],
max = {},
result = data.reduce((r, o) => {
max[o.parent] = (max[o.parent] || 0) + 1;
(r[o.parent] ??= {})[max[o.parent]] = o;
return r;
}, {});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
I think you can loop through the items and then find the parent
let arrayC = [];
arrayB.forEach(item => {
let parent = array1.find(e => e.id === item.parent);
// do something here to combine it into one object
// then arrayC.push(newItem);
});
https://codesandbox.io/s/boring-snowflake-brksj?file=/src/index.js
const result = arrayA.reduce((acc, arrayAItem) => {
return {
...acc,
[arrayAItem.id]: arrayB.filter(
(arrayBItem) => arrayBItem.parent === arrayAItem.id
)
};
}, {});
}]
You can do this with map and filter functions of array.
const newArray = array1.map(array1Item => {
return { [array1Item.id]: array2.filter(array2Item => array2Item.parent === array1Item.id)}
})
Based on the two arrays that you have, I assume that they are only linked by their "parent" ids. If this is the case, you can reduce the B array using the A array as an accumulator.
const a = [
{ "id": "606f1a2bebb5fb53804dd3d5" , "name": "cc" },
{ "id": "606f1a30cfe84430c41dce88" , "name": "bb" },
{ "id": "606f1a4ed2ff554e4ea11b82" , "name": "ff" },
];
const b = [
{ "id": "3344" , "color": "pink" , "parent": "606f1a2bebb5fb53804dd3d5" },
{ "id": "3453" , "color": "blue" , "parent": "606f1a30cfe84430c41dce88" },
{ "id": "3331" , "color": "yellow" , "parent": "606f1a4ed2ff554e4ea11b82" },
{ "id": "4442" , "color": "black" , "parent": "606f1a30cfe84430c41dce88" },
];
const c = Object
.entries(b.reduce((acc, { id, color, parent }) =>
({ ...acc, [parent]: {
...acc[parent],
colors: [...acc[parent].colors, { id, color } ]
}}),
Object.fromEntries(a.map(({ id, name }) =>
[ id, { name, colors: [] } ]))))
.map(([ id, value ]) => value);
console.log(c);
.as-console-wrapper { top: 0; max-height: 100% !important; }

filter nested array with Javascript

having an array with objects and inside an options array how do I filter the inner array of objects by key value?
Here is the following example:
let test = [{
"options": [{
"label": "Audi",
"value": 10
},
{
"label": "BMW",
"value": 18
},
{
"label": "Mercedes Benz",
"value": 116
},
{
"label": "VW",
"value": 184
}
],
"label": "test1"
},
{
"options": [{
"label": "Adler",
"value": 3664
},
{
"label": "Alfa Romeo",
"value": 3
},
{
"label": "Alpine",
"value": 4
}
],
"label": "test2"
}
]
how do I get back the object:
{
"label": "Audi",
"value": 10
}
if I filter with keyword Audi
return label.toLowerCase().includes(inputValue.toLowerCase());
I tried with the following
test.map((k) => {
res = k.options.filter((j) => {
inputValue.toLowerCase();
if (j.label.toLowerCase().includes(inputValue.toLowerCase())) {
return j;
}
});
});
You need to return the result of filter(), not just assign it to a variable, so that map() will return the results.
let test = [{
"options": [{
"label": "Audi",
"value": 10
},
{
"label": "BMW",
"value": 18
},
{
"label": "Mercedes Benz",
"value": 116
},
{
"label": "VW",
"value": 184
}
],
"label": "test1"
},
{
"options": [{
"label": "Adler",
"value": 3664
},
{
"label": "Alfa Romeo",
"value": 3
},
{
"label": "Alpine",
"value": 4
}
],
"label": "test2"
}
]
let inputValue = "audi";
let search = inputValue.toLowerCase();
let result = test.map(k => k.options.filter(j => j.label.toLowerCase().includes(search)));
console.log(result);
This will return all options matching the search query :
function find(array, query) {
return array.reduce((prev, current) => prev.concat(current.options), []).filter(item => item.label.includes(query))
}
find(test, 'Audi')
Use flatMap() followed by filter():
let test=[{options:[{label:"Audi",value:10},{label:"BMW",value:18},{label:"Mercedes Benz",value:116},{label:"VW",value:184}],label:"test1"},{options:[{label:"Adler",value:3664},{label:"Alfa Romeo",value:3},{label:"Alpine",value:4}],label:"test2"}]
let result = test.flatMap(el => {
return el.options.filter(car => car.label == "Audi")
})[0]
console.log(result)
You need to go two levels deep when traversing your array.
function filterArray(needle, haystack) {
return haystack
.map(h => h.options)
.flatMap(h => h )
.filter(h => h.label.toLowerCase().includes(needle.toLowerCase()));
CodeSandbox
This might gile your answer:
console.log(test[0].options[0]);

Dynamically create array of objects from the object with the array values

Currently I have the below object structure,
`let selectedOptions = {
"color": {
"id": "2",
"values": [
{
"value": "red",
"label": "Red",
"id": 1
},
{
"value": "blue",
"label": "Blue",
"id": 2
}
]
},
"size": {
"id": "19",
"values": [
{
"value": "medium",
"label": "Medium",
"id": 2
}
]
},
"demo": {
"id": "19",
"values": [
{
"value": "neylon",
"label": "Neylon",
"id": 2
}
]
}
.
.
.
N
}; `
And want to create array of objects from the above object like as below,
[
{ color: "red", size: "medium", demo: "neylon" },
{ color: "blue", size: "medium", demo: "neylon" }
]
I have tried like below but it didn't worked
https://jsfiddle.net/6Lvb12e5/18/
let cArr = [];
for(key in selectedOptions) {
selectedOptions[key].values.forEach(function(val,i) {
cArr.push({ [key]: val.value })
})
}
Thanks
You could take the wanted parts, like color, size and demo and build a cartesian product out of the given data.
const
cartesian = (a, b) => a.reduce((r, v) => r.concat(b.map(w => [].concat(v, w))), []),
options = { color: { id: "2", values: [{ value: "red", label: "Red", id: 1 }, { value: "blue", label: "Blue", id: 2 }] }, size: { id: "19", values: [{ value: "small", label: "Small", id: 1 }, { value: "medium", label: "Medium", id: 2 }] }, demo: { id: "19", values: [{ value: "neylon", label: "Neylon", id: 2 }] } },
parts = Object
.entries(options)
.map(([k, { values }]) => [k, values.map(({ value }) => value)]),
keys = parts.map(([key]) => key),
result = parts
.map(([, values]) => values)
.reduce(cartesian)
.map(a => Object.assign(...a.map((v, i) => ({ [keys[i]]: v }))));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
I am not sure that is this the best way to do this, and even the data is missing but as a comment, it is quite big to post but can try:
let selectedOptions = {
"color": {
"id": "2",
"values": [
{
"value": "red",
"label": "Red",
"id": 1
},
{
"value": "blue",
"label": "Blue",
"id": 2
}
]
},
"size": {
"id": "19",
"values": [
{
"value": "medium",
"label": "Medium",
"id": 2
}
]
},
"demo": {
"id": "19",
"values": [
{
"value": "neylon",
"label": "Neylon",
"id": 2
}
]
}
};
let cArr = [];
var obj = {};
var glob;
for(key in selectedOptions) {
selectedOptions[key].values.forEach(function(val,i) {
obj[key] = val.value;
}
)
glob = obj;
}
cArr.push(glob);
console.log(cArr)

Finding object through list of objects by finding a specific value

I have list of dynamic objects. Dynamic because the numeric identity of the objects under data are always changing/increasing. I already have a variable that is defined as the amount of objects under data. The variable is as follows
var numOfFruits = Object.keys(Fruits.data).length
Then to go through each object under data I run this loop
for(numOfFruits in Fruits.data) {
var objectsUnderData = Fruits.data[numOfFruits];
}
I am wanting to be able to request the numeric identity of an object containing a certain value such as "Banana", "Apple", or "Grape". So I want to be able to use a pre-defined variable like var name = "Apple" and get the identity of the object it's in.
{
"type": "fruits",
"data": {
"1": {
"id": 1,
"key": "Banana",
"name": "Banana",
},
"2": {
"id": 2,
"key": "Orange",
"name": "Orange",
},
"3": {
"id": 3,
"key": "Grape",
"name": "Grape",
},
"4": {
"id": 4,
"key": "Apple",
"name": "Apple",
}
}
}
My Question: How do I use a specific value (name: "Apple", name: "Banana", etc...) to find the identity of the object it is under.
See function fruitFinder below:
function fruitFinder (Fruits, name) {
for (fruit in Fruits.data) {
if (Fruits.data[fruit].name == name) {
return Fruits.data[fruit].id;
}
}
}
var Fruits = {
"type": "fruits",
"data": {
"1": {
"id": 1,
"key": "Banana",
"name": "Banana",
},
"2": {
"id": 2,
"key": "Orange",
"name": "Orange",
},
"3": {
"id": 3,
"key": "Grape",
"name": "Grape",
},
"4": {
"id": 4,
"key": "Apple",
"name": "Apple",
}
}
};
var name = "Apple";
console.log(fruitFinder(Fruits, name));

Categories

Resources