Related
I have the following object:
{
4: {
1: [
{ order: 1, name: 'Test 4' }
]
},
0: {
15: [
{ order: 7, name: 'Test 1' },
{ order: 3, name: 'Test 3' },
],
12: {
{ order: 1, name: 'Test 2' }
}
}
}
Essentially what I am trying to achieve is to order this by the keys and then order further by the order property from within the nested value. So in turn I get the following output:
{
0: {
12: {
{ order: 1, name: 'Test 2' }
},
15: [
{ order: 3, name: 'Test 3' },
{ order: 7, name: 'Test 1' },
]
},
4: {
1: [
{ order: 1, name: 'Test 4' }
]
}
}
I then want to completely flatten this so it's without any of the outer object and just the data within the order, the outcome would then be:
[
{ name: 'Test 2' },
{ name: 'Test 3' },
{ name: 'Test 1' },
{ name: 'Test 4' }
]
I imagine this would be some kind of recursive operation which I need to do and I originally did it with something like the following but it got a bit messy:
Object.keys(obj)
.sort()
.reduce((acc, key) => { acc[key] = obj[key]; return acc; }, {});
Anotner one sorting approach
const obj = {4:{1:[{order:1,name:'Test 4'}]},0:{15:[{order:7,name:'Test 1'},{order:3,name:'Test 3'},],12:[{order:1,name:'Test 2'}]}};
const result = Object.entries(obj).flatMap(([u1, v1]) =>
Object.entries(v1).flatMap(([u2, v2]) =>
v2.map((v3) => ({ key: u1*1_000 + u2 + v3.order/1_000, item: v3 }))
)
)
.sort(({ key: a }, { key: b }) => a - b)
.map(({ item }) => item);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0 }
You can sort each obj by keys using Object.keys(obj).sort() and then access each element by its key.
Do this 2 times to get the array of object
const obj = {
4: {
1: [
{ order: 1, name: 'Test 4' }
]
},
0: {
15: [
{ order: 7, name: 'Test 1' },
{ order: 3, name: 'Test 3' },
],
12: [
{ order: 1, name: 'Test 2' }
]
}
}
let flatItems = []
const keys = Object.keys(obj).sort()
for (const key of keys){
const subObj = obj[key]
const subKeys = Object.keys(subObj).sort()
for(const subKey of subKeys){
flatItems = flatItems.concat(subObj[subKey].sort((a, b) => a.order - b.order))
}
}
console.log(flatItems)
The integer properties (in the range of 32 bit unsigned integers) don't need sorting, as iteration over them (e.g. via Object.values) is by specification already sorted by those integer keys. So the logic needs to focus only on sorting the inner objects, and it will be fine.
const flatSort = obj => Array.isArray(obj)
? [...obj].sort((a, b) => a.order - b.order).map(a => a.name)
: Object.values(obj).flatMap(flatSort);
const obj = { 4: { 1: [ { order: 1, name: 'Test 4' } ] }, 0: { 15: [ { order: 7, name: 'Test 1' }, { order: 3, name: 'Test 3' }, ], 12: [ { order: 1, name: 'Test 2' } ] } };
const res = flatSort(obj);
console.log(res);
Object keys can’t easily be sorted, the iteration order depends on different rules and isn’t straightforward to work with. As a result you’re better off converting your object into an array of key-value pair arrays (entries), and then sort that array rather than trying to sort your object (sorting is required as your object can have non-array index keys such as -1).
To do this, you can create a recursive function (sortEntries) that takes your object and grabs the entries from it. Using the key component of the entries, you can sort based on that. Once you've sorted the entries, you can .flatMap() the result of recursively sorting your nested object value by calling the sortEntries function again. For the base case (termination case), you can stop the recursion once you've found a value that is an array, which for that you can sort by the order property for your objects and then .map() each object to extract only the name property. Each array returned by .map() will be merged together into one resulting array due to the previous .flatMap() call:
const obj = { 4: { 1: [ { order: 1, name: 'Test 4' } ] }, 0: { 15: [ { order: 7, name: 'Test 1' }, { order: 3, name: 'Test 3' }, ], 12: [ { order: 1, name: 'Test 2' } ] } };
const sortEntries = (obj) => {
return Array.isArray(obj)
? obj.slice().sort((a, b) => a.order - b.order).map(({name}) => ({name}))
: Object.entries(obj).sort(([a], [b]) => a - b).flatMap(([, val]) => sortEntries(val));
}
const res = sortEntries(obj);
console.log(res);
Full Key/Value object sorting (array values included):
function sortKeys(object) {
if (Array.isArray(object))
return object.sort().map((obj) => (typeof obj === "object" ? sortKeys(obj) : obj));
const sortedObj = {};
const keys = Object.keys(object).sort();
for (var index in keys) {
const key = keys[index];
if (typeof object[key] === "object") {
sortedObj[key] = sortKeys(object[key]);
} else {
sortedObj[key] = object[key];
}
}
return sortedObj;
}
This question already has answers here:
How to map more than one property from an array of objects [duplicate]
(7 answers)
Closed 2 years ago.
I want to create a new array from an array with id property from an array of objects using javascript.
below is the data,
const input = [
{
id: '1',
name: 'name1',
},
{
id: '2',
name: 'name2',
},
]
from the above input data, I want to create a new array like below
const output = [
{id: '1'},
{id: '2'},
]
i have tried const output= input.map((s) => s.id)
this will give output like below,
const output = ['1', '2']
but I also want the key id. how can I do it? could someone help me with this? thanks.
At first in the object, we must use "," don't use ";"
here is a simple example for your question
let input = [
{
id: '1',
name: 'name1',
},
{
id: '2',
name: 'name2',
},
];
input = input.map(({ id }) => ({ id }));
console.log(input);
const input = [
{
id: '1',
name: 'name1',
},
{
id: '2',
name: 'name2',
},
];
/* one way is to remove name property then you will left with only id */
const result1 = input.map((item) => {
delete item.name;
return item;
});
console.log('result1 : ', result1);
/* other way is to create new object with id property and assign item.id */
const result2 = input.map((item) => {
return {id: item.id};
});
console.log('result2 : ', result2);
i have a 2 object which i wan't to make filtering with es6
first is my data object and second selected some data.
I wan't to get all items in data object which have second object values
let data = [
{
id: 1,
name: 'A',
status: 1
},
{
id: 2,
name: 'B',
status: 1
},
{
id: 3,
name: 'C',
status: 3
},
{
id: 4,
name: 'D',
status: 2
}
]
and second object is :
let selectedStatus = [
{
id: 1,
status: 1
},
{
status: 3
}
]
in this case i want't to get data object items which contains same statuses in second object so in this case i need to get this result:
data = [
{
id: 1,
name: 'A',
status: 1
},
{
id: 2,
name: 'B',
status: 1
},
{
id: 3,
name: 'C',
status: 3
},
]
You can do like this:
data = data.filter(item =>
selectedStatus.map(s => s.status).includes(item.status)
);
Below snippet will give the expected answer:
var result = [];
data.forEach((value) => {
selectedStatus.forEach(val => {
if(value.status == val.status) {
result.push(value)
}
});
});
console.log(result)
Assuming you do not have any browser restrictions, you can make use of followed by using Array.includes() to check statuses on data which are on selectedStatus, followed by Array.filter() to filter out objects which match the required condition.
const data = [
{
id: 1,
name: 'A',
status: 1
},
{
id: 2,
name: 'B',
status: 1
},
{
id: 3,
name: 'C',
status: 3
},
{
id: 4,
name: 'D',
status: 2
}
]
const selectedStatus = [
{
id: 1,
status: 1
},
{
status: 3
}
];
const res = data.filter(obj => selectedStatus.map(s => s.status).includes(obj.status));
console.log(res);
let result = [];
selectedStatus.forEach(selectedStatus => result = result.concat(data.filter(status => status.status === selectedStatus.status)))
If you want to generate a third list, like your expected result, you can generate a white-list to match against.
const whiteList = selectedStatus.map((sel) => sel.status); // which gives you an array of all selected status you want to filter for
const filteredData = data.filter((data) => ~whiteList.indexOf(data.status)); // please consider that filter returns a new array that contain all items where the condition was falsy. That can be confusing.
to understand the ~ operator please check
https://wsvincent.com/javascript-tilde/
you may struggle with the result of the filtered array. Please consider that array.filter returns a new array that contain all items which did NOT met the condition in other words its a negation. That can be confusing.
https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
ES7
const filteredData = data.filter((data) => whiteList.includes(data.status));
I had a variable like that
const data = {
code: 1,
items: [
{ nickname: 1, name: [
{id : "A"},
{id : "B"}
]
},
{
nickname: 2, name: [
{id: "A"},
{id: "C"}
]
}
]
}
after that, I want to show how many characters: A:2, B:1, C:1
You can do that is following steps:
Use flatMap() on the array data.items
Inside flatMap() use map() to convert all the object to their id and return it from flatMap(). This way you will array ["A","B","A","C"]
Then use reduce() and get an object with count of all the letters.
const data = { code: 1, items: [ { nickname: 1, name: [ {id : "A"}, {id : "B"} ] }, { nickname: 2, name: [ {id: "A"}, {id: "C"} ] } ] }
const res = data.items.flatMap(x =>
x.name.map(a => a.id)
).reduce((ac,a) => (ac[a] = ac[a] + 1 || 1,ac),{});
console.log(res)
const data = {
code: 1,
items: [
{
nickname: 1,
name: [
{ id: "A" },
{ id: "B" }
]
},
{
nickname: 2,
name: [
{ id: "A" },
{ id: "C" }
]
}
]
};
const res = data.items.reduce((acc, next) => {
next.name.forEach(({ id }) => {
acc[id] = acc[id] + 1 || 1;
});
return acc;
}, {});
console.log(res);
You can do that in a single shot using reduce.
Reducing data.items will allow you to add to the accumulator (initially an empty object), the value of the currently looped name property item.
The result will be an object owning all the occurences of each encountered letter in the name property of each array.
Relevant lines explained:
data.items.reduce((acc, next) will call the reduce method on data.items. acc is the reduce accumulator (initially an empty object), next is the currently looped item of data.items.
next.name.forEach(({id}) in this line, we loop the name property of the currently looped item (data.items[n]). ({id}) is a short syntax to acquire the id property of the looped item in the foreach. It's equivalent to (item => item.id).
acc[id] = acc[id] + 1 || 1; tries to increase the property [id] of the accumulator (example: "A" of {}) by 1. If it does not exist, it sets the value to 1.
return acc; returns the accumulator.
You could iterate name and take id in a loop for assigning the count.
const
data = { code: 1, items: [{ nickname: 1, name: [{ id : "A" }, { id : "B" }] }, { nickname: 2, name: [{ id: "A" }, { id: "C" }] }] },
result = data.items.reduce(
(r, { name }) => (name.forEach(({ id }) => r[id] = (r[id] || 0 ) + 1), r),
{}
);
console.log(result);
Trying to parse one data set that has a bunch of the same "secondaryIDs" in way that i can group and iterate through them together.
In english what im trying to do is
"select a unique group of all items where the value of field is unique "
'use strict';
const data = [{
Group: 'A',
Name: 'SD'
}, {
Group: 'B',
Name: 'FI'
}, {
Group: 'A',
Name: 'MM'
}, {
Group: 'B',
Name: 'CO'
}];
let unique = [...new Set(data.map(item => item.Group))];
console.log(unique);
Which gives ["A"],["B"]
but what im looking for is
{
A: [ "SD","MM" ],
B: [ "FI","CO" ],
}
For this, I would use array.reduce instead of array.map because what you're actually hoping to return is a new value, not a modified array, the reduce method is perfect when you want to literally reduce the array into a single output value, in your case an object of unique groups. Maybe try something like this:
let unique = data.reduce((acc, { Group, Name }) => {
if (!(acc.hasOwnProperty(Group))) {
acc[Group] = [Name];
} else {
acc[Group].push(Name);
};
return acc;
}, {});
I've also added a pen for this at: https://codepen.io/anon/pen/BGpgdz?editors=1011 so you can see this working.
Hope this helps!
You can also reduce your array to the grouped object (keyed by Group values):
const data = [{
Group: 'A',
Name: 'SD'
}, {
Group: 'B',
Name: 'FI'
}, {
Group: 'A',
Name: 'MM'
}, {
Group: 'B',
Name: 'CO'
}];
const grouped = data.reduce((a, {Group, Name}) => {
if (!(Group in a)) a[Group] = [Name];
else a[Group].push(Name);
return a;
}, {});
console.log(grouped);
can do something like..
const map = {};
data.forEach( d => {
if( map[d.Group] ) {
map[d.Group].push(d.Name);
} else {
map[d.Group] = [d.Name];
}
})
console.log(map)
I think the easiest way to achieve this would be to use Array.prototype.reduce method to create an object that maps unique Group names to arrays that contain Names. You can supply an empty object literal as your initial reduce accumulator:
const data = [{
Group: 'A',
Name: 'SD'
}, {
Group: 'B',
Name: 'FI'
}, {
Group: 'A',
Name: 'MM'
}, {
Group: 'B',
Name: 'CO'
}];
var namesByGroup = data.reduce((map, el) => {
map[el.Group] ? map[el.Group].push(el.Name) : map[el.Group] = [el.Name];
return map;
}, {});
console.log(namesByGroup);
If you're interested in a functional approach, here is a solution using Ramda:
const group =
R.pipe(
R.groupBy(R.prop('Group')),
R.map(R.map(R.prop('Name'))));
console.log(
group([
{Group: 'A', Name: 'SD'},
{Group: 'B', Name: 'FI'},
{Group: 'A', Name: 'MM'},
{Group: 'B', Name: 'CO'}])
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.min.js"></script>
can also be done using forEach
const data = [{
Group: 'A',
Name: 'SD'
}, {
Group: 'B',
Name: 'FI'
}, {
Group: 'A',
Name: 'MM'
}, {
Group: 'B',
Name: 'CO'
}];
const somefunction = (data) => {
let arr = {}
data.forEach( ({Group, Name}) => {
Group in arr ? arr[Group].push(Name) : arr[Group] = [Name]
})
return arr;
}
console.log(somefunction(data))