Ordering an object with keys and subobjects by a property - javascript

I have an object with a sub-object with ids. I would like to order the subobject by a specific property but without loosing the id references.
I've tried ordering individually the subobject "options" by position using lodash and different vanilla js solutions I've found and reasigning it to the main object, but I loose the ids because in all cases it returns an array and I can't find a way to keep the same id structure.
Object example:
{
name: 'User name',
options: {
'234aafg': {
name: 'bar',
position: 2
},
'543al22': {
name: 'foo',
position: 0
},
'437uaz2': {
name: 'baz',
position: 1
},
}
}
Expected Result:
{
name: 'User name',
options: {
'543al22': {
name: 'foo',
position: 0
},
'437uaz2': {
name: 'baz',
position: 1
},
'234aafg': {
name: 'bar',
position: 2
}
}
}

let obj = {
name: 'User name',
options: {
'234aafg': {
name: 'bar',
position: 2
},
'543al22': {
name: 'foo',
position: 0
},
'437uaz2': {
name: 'baz',
position: 1
},
}
}
let keys = Object.keys(obj.options).sort((a, b) => {
if (obj.options[a].position < obj.options[b].position) return -1
return 1
})
let options = obj.options
let newOptions = {}
keys.forEach(key => newOptions[key] = options[key])
obj.options = newOptions
console.log(obj)

I would suggest generating a sorted list of keys that you can later loop over whenever you need. The looping logic would take the key and lookup the original object to get the data it needed for processing, such as displaying the data in order on the page to the user.
var data = {
name: 'User name',
options: {
'234aafg': {
name: 'bar',
position: 2
},
'543al22': {
name: 'foo',
position: 0
},
'437uaz2': {
name: 'baz',
position: 1
},
}
};
data.sortedOptions = Object.entries(data.options)
.sort(function(a, b){
return a[1].position - b[1].position;
})
.map(function(entry){
return entry[0];
});
console.log(data);

This is a simple functional approach that should accomplish what you want. The object entries are converted into an array with an additional id property. After sorting, the id properties are stripped and used to re-construct a new object in the correct order.
However, I would personally skip the call to .reduce and just use the array with the additional id property.
data.options = Object.entries(data.options)
.map(([id, value]) => ({ ...value, id }))
.sort((a, b) => a.position - b.position)
.reduce((rollup, entry) => {
const { id, ...rest } = entry;
rollup[id] = rest;
return rollup;
}, {});

Related

Add complex array of objects into another object

I have an source object obj that looks like this and an array input
const obj = {
name: "xyz",
filter: {
and: [
{
or: [
{
and: []
}
]
}
]
}
};
const input = ["test1\name1", "test2\name2"]
I need to push objects that are formed after spiltting input by \. After splitting, using left side of the string i need to form an object like this
{ type: "type1", value: whatever the left hand value}
Same for right side value
{ type: "type2", value: whatever the right hand value}
And these objects should be pushed to innermost and in the source object.
Expected output
{
name: "xyz",
filter: {
and: [
{
or: [
{
and: [
{ type: "type1", value: "test1" },
{ type: "type2", value: "name1" },
{ type: "type1", value: "test2" },
{ type: "type2", value: "name2" }
]
}
]
}
]
}
}
Code that I tried
function processResult(input) {
return {
name: "xyz",
filter: {
and: [
{
or: [
{
and: getUpdatedValues(input)
}
]
}
]
}
};
}
// I need the getUpdateValues to be processing the each item from the input array and then sending the two objects back after splitting
function getUpdatedValues(input){
const updated = input.map(item => {
const spilt = item.split("\\");
});
}
Assuming that the input array would include an escape character and could be like so: ["test1\\name1", "test2\\name2"], presented below is one possible way to achieve the desired objective.
Code Snippet
const transformMyArr = (myArr) => (
myArr.flatMap(
s => {
const [leftie, rightie] = s.split('\\');
return ([{
type: 'type1', value: leftie
}, {
type: 'type2', value: rightie
}]);
}
)
);
/* code explanation
// method to transform the array to required format
const transformMyArr = (myArr) => (
myArr.flatMap( // iterate over the array and remove nested-array in result
s => { // manipulate each array element
// "split" using "\\" and store the left-side as "type"
// and the rest as "value"
const [type, value] = s.split('\\');
// explicit return of an array with two objects per array elt
return ([{
type: 'type1', value: leftie
}, {
type: 'type2', value: rightie
}]);
}
) // implicit return from the "transformMyArr" method
);
*/
let myInputArr = ["test1\\name1", "test2\\name2"];
const myObj = {
name: "test",
filter: {
and: [{
or: [{
and: [...transformMyArr(myInputArr) ]
}]
}]
}
};
console.log('updated obj:\n', myObj);
.as-console-wrapper { max-height: 100% !important; top: 0 }
Explanation
Inline comments added to the snippet above.
EDIT
the left and right side value after splitting can be present in different items in the array too. How can I have only unique type1 , type2 objects inside final array
const myTransform2 = arr => {
// set-up empty arrays to hold left & right side elements
let leftEltArr = [], rightEltArr = [];
// iterate over the arg-array using ".forEach()"
arr?.forEach(
s => {
// split using "\\" to store left & right side elts
const [leftElt, rightElt] = s.split('\\');
// push elements into respective arrays
leftEltArr.push(leftElt);
rightEltArr.push(rightElt);
}
);
// return the result using left & right arrays
return (
([...new Set(leftEltArr)]) // remove dupes
.map(value => ({ type: 'type1', value })) // transform to required format
.concat( // concat result of similar operation on right-side
([...new Set(rightEltArr)])
.map(value => ({ type: 'type2', value }))
)
);
};
// updated sample input with 3rd elt which has duplicates
// on both left-side & right-side of the "\\"
let myInputArr = ["test1\\name1", "test2\\name2", "test1\\name2"];
const myObj = {
name: "test",
filter: {
and: [{
or: [{
and: [...myTransform2(myInputArr) ]
}]
}]
}
};
console.log('transformed object:\n', myObj);
.as-console-wrapper { max-height: 100% !important; top: 0 }
One way to do it. It's tricky because the input structure is so different to the output, and there is no reference for "type1"/"type2" other than the array element position.
const input = ["test1\\name1", "test2\\name2"];
function processResult(input) {
return {
name: "xyz",
filter: {
and: [
{
or: [
{
and: getUpdatedValues(input)
}
]
}
]
}
};
}
function getUpdatedValues(input){
return input.flatMap((item, i) => item.split("\\").map(val => ({[`type${i + 1}`]: val })));
}
console.log(processResult(input));

Retain array structure when filtering nested array

My brain froze with this advanced filtering. This task has exceeded my basic knowledge of filter, map etc.
Here I have an array with nested objects with array:
const DATA = [
{
title: 'Spongebob',
data: [
{ id: 1, name: 'Mr Crabs' },
{ id: 2, name: 'Sandy' }
]
},
{
title: 'Dragon Balls Z',
data: [
{ id: 1, name: 'GoKu' },
{ id: 2, name: 'Zamasu' }
]
}
];
You may have seen this sort of style if you've worked with React Native (RN). This question is not for RN. I need to perform a filter on the name property in the nested array and when I get a match, I must return the format as the DATA variable.
const handleFiltering = (value) => {
const _value = value.toLowerCase();
const results = DATA.map(o => {
return o.data.filter(o => o.name.toLowerCase().indexOf(_value) != -1)
});
console.log(results);
};
My limited knowledge of deep filtering returns the basic filtering for the data array but need to retain the structure for DATA. The expected results I'd expect:
// I'm now querying for "ZAMASU"
const handleFiltering = (value='ZAMA') => {
const _value = value.toLowerCase();
const results = DATA.map(o => {
return o.data.filter(o => o.name.toLowerCase().indexOf(_value) != -1)
});
// console.log(results) should now be
// [
// {
// title: 'Dragon Balls Z',
// data: [
// { id: 2, name: 'Zamasu' }
// ]
// }
// ];
};
What comes to mind is the use of {...DATA, something-here } but my brain has frozen as I need to get back the title property. How to achieve this, please?
Another solution would be first use filter to find only objects containing the name in data passed through the argument, subsequently mapping data.
Here is your adjusted filter method
const handleFiltering = (value) => {
const _value = value.toLowerCase();
const results = DATA.filter((obj) =>
obj.data.some((character) => character.name.toLowerCase() === _value)
).map((obj) => ({
title: obj.title,
data: obj.data.filter(
(character) => character.name.toLowerCase() === _value
),
}));
console.log(results);
};
You can use reduce method of array. First find out the object inside data array and then add that to accumulator array as new entry by preserving the original structure.
const DATA = [
{
title: 'Spongebob',
data: [
{ id: 1, name: 'Mr Crabs', where: 'tv' },
{ id: 2, name: 'Sandy' }
]
},
{
title: 'Dragon Balls Z',
data: [
{ id: 1, name: 'GoKu' },
{ id: 2, name: 'Zamasu' }
]
}
];
let handleFiltering = (value='tv') => {
return DATA.reduce((acc,d) => {
let obj = d.data.find(a => a.name?.toLowerCase().includes(value.toLowerCase())
|| a.where?.toLowerCase().includes(value.toLowerCase()));
obj ? acc.push({...d, data:[obj]}) : null;
return acc;
}, []);
}
let result = handleFiltering();
console.log(result);

How to dedupe an array of objects by a key value pair?

// This is a large array of objects, e.g.:
let totalArray = [
{"id":"rec01dTDP9T4ZtHL4","fields":
{"user_id":170180717,"user_name":"abcdefg","event_id":516575,
}]
let uniqueArray = [];
let dupeArray = [];
let itemIndex = 0
totalArray.forEach(x => {
if(!uniqueArray.some(y => JSON.stringify(y) === JSON.stringify(x))){
uniqueArray.push(x)
} else(dupeArray.push(x))
})
node.warn(totalArray);
node.warn(uniqueArray);
node.warn(dupeArray);
return msg;
I need my code to identify duplicates in the array by a key value of user_id within the objects in the array. Right now, my code works to identify identical objects in the array, but I need it to identify dupes based on a key value inside the objects instead. How do I do this? I am struggling to figure out how to path the for each loop to identify the dupe based on the key value instead of the entire object.
Right now, my code works to identify identical objects in the array, but I need it to identify dupes based on a key value inside the objects instead. How do I do this?
Don’t compare the JSON representation of the whole objects then, but only their user_id property specifically.
totalArray.forEach(x => {
if(!uniqueArray.some(y => y.fields.user_id === x.fields.user_id)){
uniqueArray.push(x)
} else(dupeArray.push(x))
})
You could take a Set and push to either uniques or duplicates.
var array = [
{ id: 1, data: 0 },
{ id: 2, data: 1 },
{ id: 2, data: 2 },
{ id: 3, data: 3 },
{ id: 3, data: 4 },
{ id: 3, data: 5 },
],
uniques = [],
duplicates = [];
array.forEach(
(s => o => s.has(o.id) ? duplicates.push(o) : (s.add(o.id), uniques.push(o)))
(new Set)
);
console.log(uniques);
console.log(duplicates);
.as-console-wrapper { max-height: 100% !important; top: 0; }
One way is to keep a list of ids you found so far and act accordingly:
totalArray = [
{ id: 1, val: 10 },
{ id: 2, val: 20 },
{ id: 3, val: 30 },
{ id: 2, val: 15 },
{ id: 1, val: 50 }
]
const uniqueArray = []
const dupeArray = []
const ids = {}
totalArray.forEach( x => {
if (ids[x.id]) {
dupeArray.push(x)
} else {
uniqueArray.push(x)
ids[x.id] = true
}
})
for (const obj of uniqueArray) console.log("unique:",JSON.stringify(obj))
for (const obj of dupeArray) console.log("dupes: ",JSON.stringify(obj))

Using a function to delete object properties by key name

I am trying to figure out how I would be able to create a function that would delete an object property based on its key name
const objects = [
{ name: 'Luke' },
{ foo: 'bar' },
{ name: 'Yoda' },
{ name: 'Leia' }
]
Rather than just delete.foo is there a function I could create that would delete any property that didn't have the key 'name'?
You can use Array.prototype.filter and object's hasOwnProperty to solve this problem.
const objects = [
{ name: 'Luke' },
{ foo: 'bar' },
{ name: 'Yoda' },
{ name: 'Leia' }
];
const res = objects.filter(item => item.hasOwnProperty('name'));
console.log(res);
.as-console-wrapper{min-height: 100%!important; top: 0;}
you can filter it;
objects.filter(object => object.name != 'Leia')
You could filter the array and map only objects with name property.
const
objects = [{ name: 'Luke' }, { foo: 'bar' }, { name: 'Yoda' }, { name: 'Leia' }],
result = objects
.filter(o => 'name' in o)
.map(({ name }) => ({ name }));
console.log(result);
Object has hasOwnProperty method, that you can use to filter the elements of your array
Solution
objects.filter(obj => obj.hasOwnProperty('name'))

How to get parent property in JS object?

Not sure if title is formulated correct, but I have a JS object that looks like:
parent:{
children:[
{
id: "1"
user:[
{
id: 'aa',
email:'aa#google.com'
},
{
id: 'b',
email:'bbb#google.com'
},
]
},
{
id:"2",
user: [
{
id:'aa',
email:'aa#google.com'
},
{
id:'eee',
email:'eee#google.com'
}
]
}
]
}
The object is way bigger but follows the above structure.
How would I go to get a list of topics each user is on, filtered b ID?
E.g. 'aa' participates in children 1 and 2
'b' participates in child 1
etc.
I figure it out I have to map the object but not sure how to proceed after that
Assuming, you want an object with participant as key and all topic id in an object, then you could iterate the arrays an build a property with the associated id.
var data = { project: { topics: [{ id: "1", participants: [{ id: 'aa', email: 'aa#google.com' }, { id: 'b', email: 'bbb#google.com' }, ] }, { id: "2", participants: [{ id: 'aa', email: 'aa#google.com' }, { id: 'eee', email: 'eee#google.com' }] }] } },
result = Object.create(null);
data.project.topics.forEach(function (a) {
a.participants.forEach(function (b) {
result[b.id] = result[b.id] || [];
result[b.id].push(a.id);
});
});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can write a function like this one:
function findTopicByID(id) {
let findedTopic = {};
let topics = obj.project.topics;
topics.map((topic) => {
if(parseInt(topic.id) === id) findedTopic = topic;
});
return findedTopic;
}
This function return the finded topic with the corresponding id or an empty object.
You can loop the topic array and build a new resulting array of users, if a user already exist then just update the users topic list, else create a new user with name, email, and a topic list.
let result = [];
project.topics.forEach((t) => {
t.participants.forEach((p) => {
let found = result.find((r) => r.name === p.id);
if (found) {
found.topics.push(t.id);
} else {
result.push({
name: p.id,
email: p.email,
topics: [t.id]
});
}
});
});
so now when you have the resulting array, you can just find a user and get which topics she participates in
let aa = result.find((r) => r.name === 'aa');
console.log(aa.topics);

Categories