Merging/grouping objects in ReactJS - javascript

I have the following arrays:
[{id:0,name:'Weight',option:'250'},{id:0,name:'Roast',option:'Medium'}]
[{id:0,name:'Weight',option:'250'},{id:0,name:'Roast',option:'Light'}]
I need to merge them in something like:
[{id:0,name:'Weight',options:['250']},{id:0,name:'Roast',options:['Medium','Light']}]
I tried to nest some loops also tried with merge, push and spread operators but I can't solve it
result.forEach((att) => {
let newoptions = [];
console.log('att', att.attributes);
att.attributes.forEach((id, idx) => {
console.log('id', id);
newoptions = [...newoptions, { option: id.option }];
newAttr[idx] = { name: id.name, options: newoptions };
});
});

I recommend you to concat both arrays and then iterate over it. Then reduce the items with Array.prototype.reduce():
const data1 = [{ id: 0, name: "Weight", option: "250" },{ id: 0, name: "Roast", option: "Medium" }];
const data2 = [{ id: 0, name: "Weight", option: "250" },{ id: 0, name: "Roast", option: "Light" }];
const array = [...data1, ...data2]; // concat
const result = array.reduce((o, c) => {
const exist = o.find(item => item.id === c.id && item.name === c.name);
if (!exist) {
const options = array
.filter(item => item.id === c.id && item.name === c.name)
.map(item => item.option);
o.push({ id: c.id, name: c.name, options: Array.from(new Set(options)) });
}
return o;
}, []);
console.log(result);

Use flat and forEach. Build an object with keys as name and aggregate the values.
const process = (...data) => {
const res = {};
data.flat().forEach(({ name, option, id }) => {
res[name] ??= { name, id, options: [] };
!res[name].options.includes(option) && res[name].options.push(option);
});
return Object.values(res);
};
const arr1 = [
{ id: 0, name: "Weight", option: "250" },
{ id: 0, name: "Roast", option: "Medium" },
];
const arr2 = [
{ id: 0, name: "Weight", option: "250" },
{ id: 0, name: "Roast", option: "Light" },
];
console.log(process(arr1, arr2));

Related

Being able to remove duplicate keys from an array of objects

I have a question about how I can delete the existing elements, for example, in my case "Tallas" is repeated, could you please help me? Thank you very much to those who are willing to help me to solve this problem
const data =
[ { atributos: { Tallas: [{ id: 0, name: 'XS' }, { id: 1, name: 'S' }] }}
, { atributos: { Calzado: [{ id: 0, name: '10' }, { id: 1, name: '9.5' }] }}
, { atributos: { Tallas: [{ id: 0, name: 'XS' }] }}
]
The idea is to have this json format with the last "Tallas" since it is the last one that I added through my dynamic form.
const expected =
[{ atributos: { Calzado: [{ id: 0, name: '10' }, { id: 1, name: '9.5' }] }}
, { atributos: { Tallas: [{ id: 0, name: 'XS' }] }}
]
How do I do this is there a way to do it, I've tried with filter plus the findindex but I can't get to eliminate the repetition of the json res= new.filter((arr, index, self) => index === self.findIndex( (t) => (t.attributes === arr.attributes )))
To unique the array of objects, we can use the Javascript Set module, if the array has complex nested objects, we can stringify each object before creating new Set data. this below function will unique the array of complex objects.
function unique_array(array = []) {
const newSetData = new Set(array.map((e) => JSON.stringify(e)));
return Array.from(newSetData).map((e) => JSON.parse(e));
}
this is a function that takes an array and return the same array but delete every duplicated item
function removeDuplicates(arr) {
return arr.filter((item,
index) => arr.indexOf(item) === index);
}
I didn't understant the part written in spanish so I hope this is what you are looking for
This is a solution specific to your question. this is not a generic solution.
const data = [
{
atributos: {
Tallas: [
{ id: 0, name: "XS" },
{ id: 1, name: "S" },
],
},
},
{
atributos: {
Calzado: [
{ id: 0, name: "10" },
{ id: 1, name: "9.5" },
],
},
},
{
atributos: {
Tallas: [
{ id: 0, name: "XS" },
{ id: 1, name: "S" },
],
},
},
];
function uniqueArray(array) {
const resultObject = array.reduce((acc, eachValue) => {
let keys = Object.keys(eachValue.atributos);
keys.forEach((eachKey) => {
if (!acc[eachKey]) {
acc[eachKey] = [];
}
let list = eachValue["atributos"][eachKey].map(
(each) => each.id + "-" + each.name
);
acc[eachKey].push(...list);
});
return acc;
}, {});
const resultArray = Object.keys(resultObject).reduce((acc, each) => {
let setData = Array.from(new Set(resultObject[each]));
acc.push({
atributos: {
[each]: setData.map((e) => {
return { id: e.split("-")[0], name: e.split("-")[1] };
}),
},
});
return acc;
}, []);
return resultArray;
}
const result = uniqueArray(data)
console.log("result ", JSON.stringify(result, null, 2));

get array of object value and nested array of object value

I've this nested array of object array:
const items = [
{
id: 1,
name: 'banana',
selected: true,
},
{
id: 2,
subItems: [
{
id: '2a',
name: 'apple',
selected: true,
},
{
id: '2b',
name: 'orange',
selected: false,
},
],
},
{
id: 3,
name: 'watermalon',
selected: true,
},
{
id: 4,
name: 'pear',
selected: false,
},
]
How can I get the ids base on selected property?
I manage to get the first level, I've tried
const selectedItemId = items.map(item => item.selected && item.id).filter(Boolean)
but how can I select the ids which is in the subItems? I expect the result to be [1, '2a', 3]
Flatten the array first. Be careful of using && item.id inside the mapper because that'll mean that falsey IDs (like 0, which is a reasonable starting number in some schemes) will be excluded.
const items=[{id:1,name:"banana",selected:!0},{id:2,subItems:[{id:"2a",name:"apple",selected:!0},{id:"2b",name:"orange",selected:!1}]},{id:3,name:"watermalon",selected:!0},{id:4,name:"pear",selected:!1}];
const output = items
.flatMap(item => [item].concat(item.subItems ?? []))
.filter(item => item.selected)
.map(item => item.id);
console.log(output);
You can recursively traverse all the items and select the items that have selected set to true.
const items = [
{ id: 1, name: "banana", selected: true },
{
id: 2,
subItems: [
{ id: "2a", name: "apple", selected: true },
{ id: "2b", name: "orange", selected: false },
],
},
{ id: 3, name: "watermalon", selected: true },
{ id: 4, name: "pear", selected: false },
];
function getSelectedItems(items, selectedItems = []) {
for (let item of items) {
if (item.subItems) {
getSelectedItems(item.subItems, selectedItems);
} else if (item.selected) {
selectedItems.push(item.id);
}
}
return selectedItems;
}
console.log(getSelectedItems(items));
let newArray = [];
items.forEach(i=>{
if(i.selected){
newArray.push(i.id)
}
if(i.subItems){
i.subItems.forEach(j=>{
if(j.selected){
newArray.push(j.id)
}
})
}
});
so this is bit lengthy. with 2 map loops
You can do:
const items=[{id:1,name:"banana",selected:!0},{id:2,subItems:[{id:"2a",name:"apple",selected:!0},{id:"2b",name:"orange",selected:!1}]},{id:3,name:"watermalon",selected:!0},{id:4,name:"pear",selected:!1}]
const output = items
.reduce((a, c) => [...a, c, ...(c.subItems || [])], [])
.filter(o => o.selected)
.map(({ id }) => id)
console.log(output)
Checking if a subItems array exsist in the item and recusively calling a function to extract selected Items will solve the issue.
function extractSubItems (items){
var selectItemsId = [];
selectItemsId = selectItemsId + items.map(item => {
if (item.selected===true){
return item.id;
}
if (item.subItems){
return extractSubItems(item.subItems);
}
}).filter(Boolean);
return selectItemsId
}
You can use Array#reduce in a nested fashion as follows:
const items = [ { id: 1, name: 'banana', selected: true, }, { id: 2, subItems: [ { id: '2a', name: 'apple', selected: true, }, { id: '2b', name: 'orange', selected: false, }, ], }, { id: 3, name: 'watermalon', selected: true, }, { id: 4, name: 'pear', selected: false, }, ],
output = items
.reduce(
(prev, {id,selected,subItems}) =>
subItems ?
selected ?
[...prev,id,...subItems.reduce( (p, {id:ID,selected:SEL}) => SEL ? [...p,ID] : p, [] )] :
[...prev,...subItems.reduce( (p, {id:ID,selected:SEL}) => SEL ? [...p,ID] : p, [] )] :
selected ?
[...prev,id] :
prev, []
);
console.log( output )
1 - Loop through items array
2 - if there is no subItems array then find the id of the item using condition
3 - if there is a subItems array then loop through that and find the id using condition
const result = []
items.map(item=>{
item.subItems ?
item.subItems.map(sub=>{
sub.selected && result.push(sub.id)
})
: item.selected && result.push(item.id)
})
console.log(result) // [1, "2a", 3]
This also works:
var ids = [
... items.filter(
it => it.selected || (it.subItems && it.subItems.some( sub => sub.selected ))
)
.map( it =>
it.subItems
? it.subItems.filter( it_sub => it_sub.selected ).map( it_sub => it_sub.id )
: [it.id]
)
].flat()
With resursive of subItems :
const items=[
{id:1,name:"banana",selected:!0},
{id:2,subItems:
[
{id:"2a",name:"apple",selected:!0},
{id:"2b",name:"orange",selected:!1},
{id:"2c",subItems:
[
{id:"2c1",name:"apple1",selected:!0},
{id:"2c1",name:"orange1",selected:!1}
]
},
]
},
{id:3,name:"watermalon",selected:!0},
{id:4,name:"pear",selected:!1}
];
const getSubItem = (obj) => {
let result = !obj.hasOwnProperty('subItems') ? [obj] : obj.subItems.reduce((res, item) => {
return res.concat(getSubItem(item))
}, [])
return result.filter(item => item.selected)
}
const result = items.reduce((res, item) => {
let subItem = getSubItem(item)
return res.concat(getSubItem(item))
}, [])
console.log(result)

How to convert array of objects into enum like key value pair in javascript?

I have an array
const a = [
{ name: "read-web-courses" },
{ name: "example" },
{ name: "t_gql" },
{ name: "ddddd" },
];
I am trying it to reduce it to the below given output , However I am stuck
Output
{0:"read-web-courses",1:"example",2:"t_gql",3:"ddddd"}
You could map the wanted property and assign the pairs to the object.
const
array = [{ name: "read-web-courses" }, { name: "example" }, { name: "t_gql" }, { name: "ddddd" }],
result = Object.assign({}, array.map(({ name }) => name));
console.log(result);
You can use Array.reduce like below.
const a = [
{ name: "read-web-courses" },
{ name: "example" },
{ name: "t_gql" },
{ name: "ddddd" },
];
const convert = arr => (
arr.reduce((total, value, index) => {
total[index] = value.name;
return total;
}, {})
)
console.log(convert(a));
This is accomplished using Array#reduce, where you can use the index from the reduce callback as the key of the new object:
const a = [ { name: "read-web-courses" }, { name: "example" }, { name: "t_gql" }, { name: "ddddd" }];
const res = a.reduce((r, o, i) => {
r[i] = o.name;
return r;
}, {});
console.log(res);
Also one more approach using Object#fromEntries and Array#map, where each object is converted to an array of key, value pairs:
const a = [ { name: "read-web-courses" }, { name: "example" }, { name: "t_gql" }, { name: "ddddd" }];
const res = Object.fromEntries(a.map((o, i) => [i, o.name]));
console.log(res)

How does JS filter the contents of an array without confusion

I want to filter the content of 'phone'
I have the following code:
var testList = [{
content:[
{id:1,uId:1,text:"apple", category: "phone"},
{id:2,uId:2,text:"nick",category: "life"},
{id:3,uId:3,text:"samsung",category: "phone"}],
user:[
{id:1,name: "Joe"},
{id:2,name: "red"},
{id:3,name: "blue"}
]
}]
const newArrys = testList
.filter((element) =>
element.content.some((subElement) => subElement.category == "phone"))
.map(element => {
return Object.assign({}, element, {
content: element.content.filter(subElement => subElement.category == "phone")
});
});
console.log(newArrys);
These are the returns:
[{
content:[
{id:1,uId:1,text:"apple", category: "phone"},
{id:3,uId:3,text:"samsung",category: "phone"}],
user:[
{id:1,name: "Joe"},
{id:2,name: "red"},
{id:3,name: "blue"}
]
}]
I want the result:
[{
content:[
{id:1,uId:1,text:"apple", category: "phone"},
{id:3,uId:3,text:"samsung",category: "phone"}],
user:[
{id:1,name: "Joe"},
{id:3,name: "blue"}
]
}]
How should I filter 'user'? , and correspond to 'content'
Can you help me? thank you
You can build a Set of id's that are having category as phone and then filter values based on the Set
var testList = [{content:[{id:1,uId:1,text:"apple", category: "phone"},{id:2,uId:2,text:"nick",category: "life"},{id:3,uId:3,text:"samsung",category: "phone"}],
user:[{id:1,name: "Joe"},{id:2,name: "red"},{id:3,name: "blue"}]}]
let final = testList.map(v => {
let mapper = new Set(v.content.filter(({
category
}) => category === "phone").map(({
id
}) => id))
return Object.fromEntries(Object.entries(v).map(([key, value]) => {
value = value.filter(({
id
}) => mapper.has(id))
return [key, value]
}).filter(([_, value]) => value))
})
console.log(final)
if Object.fromEntries is not supported
var testList = [{content:[{id:1,uId:1,text:"apple", category: "phone"},{id:2,uId:2,text:"nick",category: "life"},{id:3,uId:3,text:"samsung",category: "phone"}],
user:[{id:1,name: "Joe"},{id:2,name: "red"},{id:3,name: "blue"}]}]
let final = testList.map(v => {
let mapper = new Set(v.content.filter(({
category
}) => category === "phone").map(({
id
}) => id))
return Object.entries(v).map(([key, value]) => {
value = value.filter(({
id
}) => mapper.has(id))
return [key, value]
}).filter(([_, value]) => value).reduce((op,[k,v])=>{
op[k] = v
return op
},{})
})
console.log(final)
You could filter the indices first and then filter by wanted indices.
var testList = [{ content:[{ id: 1, uId: 1, text: "apple", category: "phone" }, { id: 2, uId: 2, text: "nick", category: "life" }, { id: 3, uId: 3, text: "samsung", category: "phone" }], user:[{ id: 1, name: "Joe" }, { id: 2, name: "red" }, { id: 3, name: "blue" }] }],
result = testList.map(o => {
var indices = [...o.content.keys()].filter(i => o.content[i].category === 'phone');
return Object.assign({}, ...['content', 'user'].map(k => ({ [k]: o[k].filter((_, i) => indices.includes(i)) })));
});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Filter the content to find the category items and after filter the users in the list intens found.
var list = [
{
content:[
{id:1,uId:1,text:"apple", category: "phone"},
{id:2,uId:2,text:"nick",category: "life"},
{id:3,uId:3,text:"samsung",category: "phone"}
],
user:[
{id:1,name: "Joe"},
{id:2,name: "red"},
{id:3,name: "blue"}
]
}
];
const result = list.map(items => {
const content = items.content.filter(c => c.category === 'phone');
const users = items.user.filter(u => content.find(c => u.id === c.id));
return {
content: content,
user: users,
}
});
console.log(result);

Get list of duplicate objects in an array of objects

I am trying to get duplicate objects within an array of objects. Let's say the object is like below.
values = [
{ id: 10, name: 'someName1' },
{ id: 10, name: 'someName2' },
{ id: 11, name: 'someName3' },
{ id: 12, name: 'someName4' }
];
Duplicate objects should return like below:
duplicate = [
{ id: 10, name: 'someName1' },
{ id: 10, name: 'someName2' }
];
You can use Array#reduce to make a counter lookup table based on the id key, then use Array#filter to remove any items that appeared only once in the lookup table. Time complexity is O(n).
const values = [{id: 10, name: 'someName1'}, {id: 10, name: 'someName2'}, {id: 11, name:'someName3'}, {id: 12, name: 'someName4'}];
const lookup = values.reduce((a, e) => {
a[e.id] = ++a[e.id] || 0;
return a;
}, {});
console.log(values.filter(e => lookup[e.id]));
Let's say you have:
arr = [
{ id:10, name: 'someName1' },
{ id:10, name: 'someName2' },
{ id:11, name: 'someName3' },
{ id:12, name: 'someName4' }
]
So, to get unique items:
unique = arr
.map(e => e['id'])
.map((e, i, final) => final.indexOf(e) === i && i)
.filter(obj=> arr[obj])
.map(e => arr[e]);
Then, result will be
unique = [
{ id:10, name: 'someName1' },
{ id:11, name: 'someName3' },
{ id:12, name: 'someName4' }
]
And, to get duplicate ids:
duplicateIds = arr
.map(e => e['id'])
.map((e, i, final) => final.indexOf(e) !== i && i)
.filter(obj=> arr[obj])
.map(e => arr[e]["id"])
List of IDs will be
duplicateIds = [10]
Thus, to get duplicates objects:
duplicate = arr.filter(obj=> dublicateIds.includes(obj.id));
Now you have it:
duplicate = [
{ id:10, name: 'someName1' },
{ id:10, name: 'someName2' }
]
Thanks https://reactgo.com/removeduplicateobjects/
You haven't clarified whether two objects with different ids, but the same "name" count as a duplicate. I will assume those do not count as a duplicate; in other words, only objects with the same id will count as duplicate.
let ids = {};
let dups = [];
values.forEach((val)=> {
if (ids[val.id]) {
// we have already found this same id
dups.push(val)
} else {
ids[val.id] = true;
}
})
return dups;
With lodash you can solve this with filter and countBy for complexity of O(n):
const data = [{ id: 10,name: 'someName1' }, { id: 10,name: 'someName2' }, { id: 11,name: 'someName3' }, { id: 12,name: 'someName4' } ]
const counts = _.countBy(data, 'id')
console.log(_.filter(data, x => counts[x.id] > 1))
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script>
You could do the same with ES6 like so:
const data = [{ id: 10,name: 'someName1' }, { id: 10,name: 'someName2' }, { id: 11,name: 'someName3' }, { id: 12,name: 'someName4' } ]
const countBy = (d, id) => d.reduce((r,{id},i,a) => (r[id] = a.filter(x => x.id == id).length, r),{})
const counts = countBy(data, 'id')
console.log(data.filter(x => [x.id] > 1))
You can use an array to store unique elements and use filter on values to only return duplicates.
const unique = []
const duplicates = values.filter(o => {
if(unique.find(i => i.id === o.id && i.name === o.name)) {
return true
}
unique.push(o)
return false;
})
With lodash you can use _.groupBy() to group elements by their id. Than _.filter() out groups that have less than two members, and _.flatten() the results:
const values = [{id: 10, name: 'someName1'}, {id: 10, name: 'someName2'}, {id: 11, name:'someName3'}, {id: 12, name: 'someName4'}];
const result = _.flow([
arr => _.groupBy(arr, 'id'), // group elements by id
g => _.filter(g, o => o.length > 1), // remove groups that have less than two members
_.flatten // flatten the results to a single array
])(values);
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
An alternative based in #ggorlen solution with new Map() as accumulator (for better performance) and without unary operator ++ (not advised by default in projects with ESLint).
const values = [{ id: 10, name: "someName1" }, { id: 10, name: "someName2" }, { id: 11, name: "someName3" }, { id: 12, name: "someName4" },];
const lookup = values.reduce((a, e) => {
a.set(e.id, (a.get(e.id) ?? 0) + 1);
return a;
}, new Map());
console.log(values.filter(e => lookup.get(e.id) > 1));
Try this
function checkDuplicateInObject(propertyName, inputArray) {
var seenDuplicate = false,
testObject = {};
inputArray.map(function(item) {
var itemPropertyName = item[propertyName];
if (itemPropertyName in testObject) {
testObject[itemPropertyName].duplicate = true;
item.duplicate = true;
seenDuplicate = true;
}
else {
testObject[itemPropertyName] = item;
delete item.duplicate;
}
});
return seenDuplicate;
}
referred from : http://www.competa.com/blog/lets-find-duplicate-property-values-in-an-array-of-objects-in-javascript/

Categories

Resources