This question already has answers here:
merge partially duplicated arrays in javascript (lodash)
(4 answers)
Group array items using object
(19 answers)
Closed 3 years ago.
I have a problem with javascript code
I have an array of the type
var array = [{
name: "Name 1",
id: 1
},
{
name: "Name 2",
id: 2
},
{
name: "Name 3",
id: 3
},
{
name: "Name 1",
id: 4
},
{
name: "Name 1",
id: 5
},
{
name: "Name 2",
id: 6
},
];
I'd like to get this:
var newArray = [{
name: "Name 1",
id: [1, 4, 5]
},
{
name: "Name 2",
id: [2, 6]
},
{
name: "Name 3",
id: 3
},
];
But I can't
I tried with a .find() but it doesn't work
Can you give me some leads please
You can use .reduce() for that. This example produces exactly the same output you expect:
var array = [
{
name: "Name 1",
id: 1
},
{
name: "Name 2",
id: 2
},
{
name: "Name 3",
id: 3
},
{
name: "Name 1",
id: 4
},
{
name: "Name 1",
id: 5
},
{
name: "Name 2",
id: 6
}
];
var output = array.reduce(function(res, curr) {
var existing = res.find(function(el) { return el.name === curr.name; });
if (existing) {
if (Array.isArray(existing.id))
existing.id.push(curr.id);
else
existing.id = [existing.id, curr.id]
} else {
res.push(curr);
}
return res;
}, []);
console.log(output);
You can use reduce() to create an object and then use Object.values to get an array. You can use map() after this to remove the array for those elements which have only a single id.
var array = [{
name: "Name 1",
id: 1
},
{
name: "Name 2",
id: 2
},
{
name: "Name 3",
id: 3
},
{
name: "Name 1",
id: 4
},
{
name: "Name 1",
id: 5
},
{
name: "Name 2",
id: 6
},
];
const res = Object.values(array.reduce((ac, a) => {
if(!ac[a.name]){
ac[a.name] = {...a, id: []}
}
ac[a.name].id.push(a.id);
return ac;
},{})).map(x => ({...x, id: x.id.length === 1 ? x.id[0] : x.id}))
console.log(res)
You can use reduce with a Map, for O(n) performance, as there are no nested loops:
const array = [
{ name: "Name 1", id: 1 },
{ name: "Name 2", id: 2 },
{ name: "Name 3", id: 3 },
{ name: "Name 1", id: 4 },
{ name: "Name 1", id: 5 },
{ name: "Name 2", id: 6 }
]
const out = [...array.reduce((a, o) => {
const e = a.get(o.name)
return (a.set(o.name, e ? Array.isArray(e) ? e.concat(o.id): [e, o.id] : o.id), a)
}, new Map())].map(([k, v]) => ({ name: k, id: v }))
console.log(out)
Try with Array.reduce() like this :
var array = [{
name: "Name 1",
id: 1
},
{
name: "Name 2",
id: 2
},
{
name: "Name 3",
id: 3
},
{
name: "Name 1",
id: 4
},
{
name: "Name 1",
id: 5
},
{
name: "Name 2",
id: 6
},
];
var output = array.reduce((acc, current) => {
if (exist = acc.find(p => p.name == current.name)) {
if(!Array.isArray(exist.id)){
exist.id = [exist.id];
}
exist.id.push(current.id);
} else {
acc.push(current)
}
return acc;
}, []);
console.log(output);
Related
This question already has answers here:
Group array items using object
(19 answers)
Flatten objects in array
(6 answers)
Closed 1 year ago.
I have some data in a non desirable format and I would like to flatten it.
Data:
[
{
team: "Team 1",
name: "John"
},
{
team: "Team 1",
name: "Stacy"
},
{
team: "Team 1",
name: "Jason"
},
{
team: "Team 2",
name: "Tim"
},
{
team: "Team 2",
name: "Andrew"
},
{
team: "Team 2",
name: "Steve"
}
,
{
team: "Team 3",
name: "Eric"
},
{
team: "Team 3",
name: "Frank"
},
{
team: "Team 3",
name: "Cory"
}
]
The desired result is:
[
{
team: "Team 1",
name: ["John", "Stacy", "Jason"],
count: 3
},
{
team: "Team 2",
name: ["Tim", "Andrew", "Steve"],
count: 3
},
{
team: "Team 3",
name: ["Eric", "Frank", "Cory"],
count: 3
}
]
I've tried looping through it and using Object.assing but that seemed the be the incorrect approach. Any suggestions on a good approach to flatted this data? Thanks
You can use Array.reduce.
In the reducer function, check whether the accumulator contains an item with the same team property. If so, increment its's count property and push the current item's name property to it's name property.
const arr=[{team:"Team 1",name:"John"},{team:"Team 1",name:"Stacy"},{team:"Team 1",name:"Jason"},{team:"Team 2",name:"Tim"},{team:"Team 2",name:"Andrew"},{team:"Team 2",name:"Steve"},{team:"Team 3",name:"Eric"},{team:"Team 3",name:"Frank"},{team:"Team 3",name:"Cory"}];
const res = arr.reduce((a,b) => {
let itm = a.find(e => e.team == b.team);
if(itm){
itm.count++;
itm.name.push(b.name);
}else{
a.push({team: b.team, name: [b.name], count:1})
}
return a;
}, [])
console.log(res)
A more efficient solution:
const arr=[{team:"Team 1",name:"John"},{team:"Team 1",name:"Stacy"},{team:"Team 1",name:"Jason"},{team:"Team 2",name:"Tim"},{team:"Team 2",name:"Andrew"},{team:"Team 2",name:"Steve"},{team:"Team 3",name:"Eric"},{team:"Team 3",name:"Frank"},{team:"Team 3",name:"Cory"}];
const obj = arr.reduce((a,b) => {
let itm = a[b.team]
if(itm){
itm.count++;
itm.name.push(b.name);
}else{
a[b.team] = {team: b.team, name: [b.name], count: 0}
}
return a;
}, {})
const res = Object.keys(obj).map(e => obj[e])
console.log(res)
Is it possible to filter this list where iseidaCcaa.id is equal to a given number:
var centers = [
{
id: 2,
nombre: "Centro 2",
iseidaCcaa: {
id: 1,
},
},
{
id: 3,
nombre: "Centro 3",
iseidaCcaa: {
id: 1,
},
},
{
id: 1,
nombre: "Centro 1",
iseidaCcaa: {
id: 2,
},
},
];
I tried this solution but it is not working
this.centers = this.centers.filter(element => {
return element.isiedaCcaa.id == 1;
})
You have a typo on the return.
should be return element.iseidaCcaa.id == 1
Although I would consider using === if the id is going to be int
You wrong property by type iseidaCcaa not isiedaCcaa
var centers = [ {
"id":2,
"nombre":"Centro 2",
"iseidaCcaa":{
"id":1,
}
},
{
"id":3,
"nombre":"Centro 3",
"iseidaCcaa":{
"id":1,
}
},
{
"id":1,
"nombre":"Centro 1",
"iseidaCcaa":{
"id":2,
}
}
]
var centers = centers.filter(element => {
return element.iseidaCcaa.id == 1;
})
console.log(centers);
I need to sort an array of objects so items with similar categories are grouped but not losing original index of categories
I want to sort/group by the category key but keep the order the categories appeared in in the array. There isn't a specific order for the categories.
arr = [
{
name: "Name 1",
category: "D Category"
},
{
name: "Name 2",
category: "A Category"
},
{
name: "Name 3",
category: "D Category"
},
{
name: "Name 4",
category: "G Category"
},
{
name: "Name 5",
category: "A Category"
}
];
attempt doesnt quite work,
it ends up putting items with same categories together but alphabetizes them, which i dont want
arr.sort((first, second) => {
const firstId = first.category;
const secondId = second.category;
if (firstId < secondId) {
return -1;
}
if (firstId > secondId) {
return 1;
}
return 0;
});
expected result:
expectedResult = [
{
name: "Name 1",
category: "D Category"
},
{
name: "Name 3",
category: "D Category"
},
{
name: "Name 2",
category: "A Category"
},
{
name: "Name 5",
category: "A Category"
},
{
name: "Name 4",
category: "G Category"
}
];
Doing a .sort with comparison partially works where similar categories are together but they end up getting alphabetized which I don't want
Here's an approach that might work for you. Basically, it does what you're attempting, but introduces an intermediary step of creating a priority map. This map will contain the unique category values (from the original data) as keys, with the order they appear as the value. You can then simply base the sort logic off of that. My code maybe be more clear than my explaination:
//Original data
const arr = [{
name: "Name 1",
category: "D Category",
}, {
name: "Name 2",
category: "A Category"
}, {
name: "Name 3",
category: "D Category"
}, {
name: "Name 4",
category: "G Category"
}, {
name: "Name 5",
category: "A Category"
}];
// A priority map so we have a basis for sorting
const orderPriority = arr
.map(o => o.category)
.reduce((map, category, idx) => {
if (map[category] == null) {
map[category] = idx;
}
return map;
}, {});
// Now implement the sort based on the priorty map we created:
arr.sort((a, b) => orderPriority[a.category] - orderPriority[b.category]);
console.log(arr);
You could first use reduce method to group them by category and also save each category index and then sort by index and use flatMap to get values.
const arr = [{"name":"Name 1","category":"D Category"},{"name":"Name 2","category":"A Category"},{"name":"Name 3","category":"D Category"},{"name":"Name 4","category":"G Category"},{"name":"Name 5","category":"A Category"}]
const grouped = arr.reduce((r, e, index) => {
if(!r[e.category]) r[e.category] = {index, values: [e]}
else r[e.category].values.push(e)
return r;
}, {})
const result = Object.values(grouped)
.sort((a, b) => a.index - b.index)
.flatMap(e => e.values)
console.log(result)
You need to iterate the array in advance and collect the groups and get them an order value.
var array = [{ name: "Name 1", category: "D Category" }, { name: "Name 2", category: "A Category" }, { name: "Name 3", category: "D Category" }, { name: "Name 4", category: "G Category" }, { name: "Name 5", category: "A Category" }],
order = 0,
hash = array.reduce((o, { category }) => (o[category] = o[category] || ++order, o), {});
array.sort(({ category: a }, { category: b }) => hash[a] - hash[b]);
console.log(array);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Another approach by using a Map and flat the values of it.
var array = [{ name: "Name 1", category: "D Category" }, { name: "Name 2", category: "A Category" }, { name: "Name 3", category: "D Category" }, { name: "Name 4", category: "G Category" }, { name: "Name 5", category: "A Category" }];
array = Array
.from(array
.reduce((m, o) => m.set(o.category, [...(m.get(o.category) || []), o]), new Map)
.values()
)
.flat();
console.log(array);
.as-console-wrapper { max-height: 100% !important; top: 0; }
This solution populates a Map, and then flattens the result into an Array. The reason I use a Map and not an object, is that objects in JavaScript should not really be thought of as ordered.
const arr = [{ name: "Name 1", category: "D Category", other: {} },
{ name: "Name 2", category: "A Category", other: {} },
{ name: "Name 3", category: "D Category", other: {} },
{ name: "Name 4", category: "G Category", other: {} },
{ name: "Name 5", category: "A Category", other: {} }]
const collate = ({arr, field}) =>
[...arr.reduce((acc,o) =>
(!acc.has(o[field])
? acc.set(o[field], [o])
: acc.set(o[field], [...acc.get(o[field]), o]),
acc), new Map).values()].flat()
console.log(collate({arr, field: 'category'}))
How to group multidimensional array objects by value in the component.
I need to group array by page and get the text for every page. I tray to use the map and reduce it but don't get good results. I work on react-native project. JS function groups doesn't work for some reason.
What is the best way to write code for this array?
I have this array:
[
{
number: 1,
englishName: 'Name 1',
items: [
{
text: 'Some text',
page: 1,
},
{
text: 'Some text',
page: 1,
},
],
},
{
number: 2,
englishName: 'Name 2',
items: [
{
text: 'Some text',
page: 2,
},
{
text: 'Some text',
page: 2,
},
],
},
{
number: 3,
englishName: 'Name 3',
items: [
{
text: 'Some text',
page: 3,
},
{
text: 'Some text',
page: 4,
},
],
},
]
I need an array like this
[
{
'page 1': [
{
text: 'Some text',
englishName: 'Name 1',
},
{
text: 'Some text',
englishName: 'Name 2',
},
],
},
{
'page 2': [
{
text: 'Some text',
englishName: 'Name 2',
},
{
text: 'Some text',
englishName: 'Name 2',
},
],
},
....
]
Seems like this will work for you.
This will give you an object:
const allPages = array.reduce((acc, curr) => {
curr.items.forEach((page) => {
var pageKey = `page ${page.page}`
if(!acc[pageKey]){
acc[pageKey] = []
}
acc[pageKey].push({ text: page.text, englishName: curr.englishName })
})
return acc
}, {})
You can convert that object into an array as well:
const finalArr = Object.entries(allPages).map(([page, array]) => {
return {
[page]: array
}
})
See example:
var array = [{
"number": 1,
"englishName": "Name 1",
"items": [
{
"text": "Some text",
"page": 1,
},
{
"text": "Some text",
"page": 1,
}
]
},
{
"number": 2,
"englishName": "Name 2",
"items": [
{
"text": "Some text",
"page": 2,
},
{
"text": "Some text",
"page": 2,
}
]
},
{
"number": 3,
"englishName": "Name 3",
"items": [
{
"text": "Some text",
"page": 3,
},
{
"text": "Some text",
"page": 4,
}
]
}
]
const allPages = array.reduce((acc, curr) => {
curr.items.forEach((page) => {
var pageKey = `page ${page.page}`
if(!acc[pageKey]){
acc[pageKey] = []
}
acc[pageKey].push({ text: page.text, englishName: curr.englishName })
})
return acc
}, {})
const finalArr = Object.entries(allPages).map(([page, array]) => {
return {
[page]: array
}
})
console.log(allPages)
console.log(finalArr)
{service: {
categories: [
{
name: "category1",
id: 1,
questions: [
{
id: 1,
question: "example trivia question here",
value: 2
}, {
id: 2,
question: "example trivia question here",
value: 3
}]
},
{
name: "category2",
id: 2,
questions: [
{
id: 3,
question: "example trivia question here",
value: 5
}
]
},
{
name: "category3",
id: 3
}
]
}};
For each category, I'm trying to count the answered trivia question. How do I get the sum of answered questions for each category (indicated when there is a value present)? I think there's something gone wrong in my loop. I've also tried this with array prototypes. Sorry for the noob question.
$scope.questionCount = 0;
angular.forEach($scope.service, function(categories, questions) {
for (var i = 0; i < questions.length; i++) {
if (questions[i].value !== null){
triviaCount = 0
triviaCount += 1
$scope.questionCount.push(triviaCount)
}
}
});
Use map() method provided by Array
var service = {
categories: [
{
name: "category1",
id: 1,
questions: [
{
id: 1,
question: "example trivia question here",
value: 2
}, {
id: 2,
question: "example trivia question here",
value: 3
}]
},
{
name: "category2",
id: 2,
questions: [
{
id: 3,
question: "example trivia question here",
value: 5
}
]
},
{
name: "category3",
id: 3
}
]
};
var result = service.categories.map(function(cat) {
return {
'name' : cat.name,
'count' : !!cat.questions ? cat.questions.length : 0
}
});
console.log(result);
// [{ name="category1", count=2}, { name="category2", count=1}, { name="category3", count=0}]
I am not used to with angular js but this is how i will do with js. i hope this helps you.
var json = {
service: {
categories: [{
name: "category1",
id: 1,
questions: [{
id: 1,
question: "example trivia question here",
value: 2
}, {
id: 2,
question: "example trivia question here",
value: 3
}]
}, {
name: "category2",
id: 2,
questions: [{
id: 3,
question: "example trivia question here",
value: 5
}]
}, {
name: "category3",
id: 3
}]
}
};
var qCount = 0;
categories = json.service.categories;
for (var category in categories) {
var temp = categories[category];
if (temp.questions !== undefined) {
var questions = temp.questions;
for (var que in questions) {
if (questions[que].value !== undefined)
qCount++;
}
}
}
console.log(qCount); //here is your question count
something like this? (look at the console)
console output:
category1 # of quesitons: 2
category2 # of quesitons: 1
category3 # of quesitons: 0
var service = {
categories: [
{
name: "category1",
id: 1,
questions: [
{
id: 1,
question: "example trivia question here",
value: 2
}, {
id: 2,
question: "example trivia question here",
value: 3
},
{
id: 9,
question: "example trivia question here",
value: '',
}
]
},
{
name: "category2",
id: 2,
questions: [
{
id: 3,
question: "example trivia question here",
value: 5
},
{
id: 7,
question: "example trivia question here",
value: '',
}
]
},
{
name: "category3",
id: 3,
questions: [],
}
]
};
$(document).ready(function() {
for(var i = 0; i < service.categories.length; i++ ){
var questionCount = 0;
for(var j = 0; j < service.categories[i].questions.length; j++ ) {
if (service.categories[i].questions[j].value != '' && service.categories[i].questions[j].value != undefined) {
questionCount++;
}
}
console.log(service.categories[i].name, ' # of quesitons: ', questionCount);
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>