Sort an array of objects dynamically javascript - javascript

I am having an array with the below items. I need to sort the below array to the array that is shown in sorted items,so that all the value with the rules can be together and the ELIG_DATABASE should be grouped with the ELIG_SERVICE.
const items =[{"name":"ELIG_DATABASE","ready":true},
{"name":"ELIG_RULES_SERVICE","ready":true},
{"name":"ELIG_GATEWAY","ready":true},
{"name":"ELIG_GATEWAY_LATEST","ready":true,"latest":true},
{"name":"ELIG_SERVICE_LATEST","ready":true,"latest":true},
{"name":"ELIG_SERVICE","ready":true},
{"name":"HDXTS","ready":false},
{"name":"RULES_VERSION","ready":true},];
I want to achieve this array so that values in the name property that has rules can be together,gateway things should be together, elig service thing should be together just that ELIG_DATABASE should be grouped together with elig service and then all other values in the name property can be sorted alphabetically.
const sortedItems =[
{"name":"ELIG_GATEWAY","ready":true},
{"name":"ELIG_GATEWAY_LATEST","ready":true,"latest":true},
{"name":"ELIG_RULES_SERVICE","ready":true},
{"name":"RULES_VERSION","ready":true},
{"name":"ELIG_DATABASE","ready":true},
{"name":"ELIG_SERVICE_LATEST","ready":true,"latest":true},
{"name":"ELIG_SERVICE","ready":true},
{"name":"HDXTS","ready":false}
];
I tried using this code but that sorts alphabetically putting ELIG_DATABASE in first position.Could any one please help on how to achieve this array in minimum code as possible.
items.sort((svcA, svcB) => {
const serviceA = svcA.name.toUpperCase();
const serviceB = svcB.name.toUpperCase();
return serviceA.localeCompare(serviceB);
});

You could take the wanted groups first in an array, sort the data and assign the object to the group or to the end of a temp array and get the flat data as result.
var data = [{ name: "ELIG_DATABASE", ready: true }, { name: "ELIG_RULES_SERVICE", ready: true }, { name: "ELIG_GATEWAY", ready: true }, { name: "ELIG_GATEWAY_LATEST", ready: true, latest: true }, { name: "ELIG_SERVICE_LATEST", ready: true, latest: true }, { name: "ELIG_SERVICE", ready: true }, { name: "HDXTS", ready: false }, { name: "RULES_VERSION", ready: true }],
together = [['GATEWAY'], ['RULES'], ['ELIG_DATABASE', 'ELIG_SERVICE']],
groups = { GATEWAY: [], RULES: [], ELIG_DATABASE: [] },
temp = [groups.GATEWAY, groups.RULES, groups.ELIG_DATABASE],
result;
for (let o of data.sort(({ name: a }, { name: b }) => a.localeCompare(b))) {
let target = together.find(a => a.some(v => o.name.includes(v)));
if (target) groups[target[0]].push(o);
else temp.push(o);
}
result = temp.flat();
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

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));

Finding a value in nested array then returning a value from the upper scope

What I am trying to achieve is:
Find if the text object within array is empty.
If criteria from no1 is matched, then return id value that sits in the top level of that object.
https://codesandbox.io/s/cranky-swirles-gb6ct?file=/src/App.js:410-412
In the code sandbox's example I have added two objects, with empty text strings and in that case I would expect to get an array of strings back (result = ['662e4120', '782h7a9x'])
I am able to find empty values, however I am not sure how to return object from the upper scope.
If you can't access the codeSandbox, snippet is attached just below:
const array = [
{
id: "5548d3c2",
state: {
properties: [
{
text: "text",
key: "fs5a"
}
]
}
},
{
id: "662e4120",
state: {
properties: [
{
text: "",
key: "m03n"
}
]
}
},
{
id: "782h7a9x",
state: {
properties: [
{
text: "",
key: "y5x1"
}
]
}
}];
const findItem = () => {
return array
.map((item) => item.state)
.map((item) => item.properties)
.flat()
.filter((item) => item.text === "");
};
Try to do something like this https://codesandbox.io/s/jovial-mcnulty-2fwh4
export default function App() {
const array = [
{
id: "5548d3c2",
state: {
properties: [
{
text: "text",
key: "fs5a"
}
]
}
},
{
id: "662e4120",
state: {
properties: [
{
text: "",
key: "m03n"
}
]
}
},
{
id: "782h7a9x",
state: {
properties: [
{
text: "",
key: "y5x1"
}
]
}
}
];
const findItem = () => {
return array.filter(obj=>obj.state.properties[0].text==="").map(obj=>obj.id)
};
console.log(findItem());
return <div className="App"></div>;
}
Here, we are filtering on the original array based on a predicate which is obj=>obj.state.properties[0].text==="". This basically get all the elements of the array which satisfy this predicate function. After this we are just applying map over the result to get the ids of the array elements satisfying this predicate function.
To get an array with the ids of the objects with no text you have to change the order or your iterations.
.filter() the array for the elements with empty text fields.
.map() the remaining elements to the values you are aiming for
When mapping or filtering you can't just go one but as many levels deep as you like. Since 'properties' holds an array and you want the first element, you can access that with the index array[0] (with that the flat() you did is superfluous)
const findItem = () => {
return array
.filter(item => item.state.properties[0].text === "") // (2) [{…}, {…}] the original items with no text
.map(item => item.id) // ['662e4120', '782h7a9x']
};
(code might be as well embedded as a snippet, which can be run directly)
const array = [
{
id: "5548d3c2",
state: {
properties: [
{
text: "text",
key: "fs5a"
}
]
}
},
{
id: "662e4120",
state: {
properties: [
{
text: "",
key: "m03n"
}
]
}
},
{
id: "782h7a9x",
state: {
properties: [
{
text: "",
key: "y5x1"
}
]
}
}];
const findItem = () => {
return array
.filter(item => item.state.properties[0].text === "") // (2) [{…}, {…}] the original items with no text
.map(item => item.id) // ['662e4120', '782h7a9x']
};
console.log(findItem())

Using an array of indices to access and modify arbitrarily deep nested arrays in Javascript

I have an array of objects. Each object can also contain an array of objects, and so on to an arbitrary depth.
var myArray = [
{
id:'foo',
items:[]
},
{
id:'bar',
items:[
{
id:'blah'
items:[...etc...]
}
]
}
]
I'd like to read, add, and remove objects in the nested arrays using an array of indices.
So a function to remove the value myArray[1][3][2] from myArray would be called with this array of indexes as a parameter: [1, 3, 2]
I've found that you can use reduce() to return a value like so:
indices.reduce((acc, cur) => Array.isArray(acc) ? acc[cur] : acc.items[cur], myArray)
but cannot work out how to remove or add a value using the same idea. Any help is much appreciated.
You could create a function which takes similar arguments as the splice function. Pass the nested array, the indices path, the total number of items to be deleted and collect all the new items to be added at the end using rest parameters.
function deepSplice(array, indices, deleteCount, ...toBeInserted) {
const last = indices.pop();
const finalItems = indices.reduce((acc, i) => acc[i].items, array);
finalItems.splice(last, deleteCount, ...toBeInserted);
return array
}
Remove the last index from the indices array.
reduce the indices array to get the nested items array in every loop to get the final items array you want to do the insert/delete operation on.
Use splice on the last index to insert/delete based on the argument passed.
If you just want to insert, pass deleteCount = 0. And if you just want to remove, skip the last argument.
Here's a snippet:
const myArray = [
{ id: "0", items: [] },
{
id: "1",
items: [
{
id: "1.0",
items: [
{ id: "1.0.0", items: [] },
{ id: "1.0.1", items: [] }]
},
{ id: "1.1", items: [] }
]
}
];
function deepSplice(array, indices, deleteCount, ...toBeInserted) {
const last = indices.pop();
const finalItems = indices.reduce((acc, i) => acc[i].items, array);
finalItems.splice(last, deleteCount, ...toBeInserted);
return array
}
console.log(
// removes "1.0.1" item and inserts a new object there
deepSplice(myArray, [1,0,1], 1, { id: 'newlyInserted'})
)
The easiest way is to use only the indices without the last one to have it used for any operation as you want.
For deleting, you need this index to splice the array, and as well for updating.
In this case, you could return the parent object and use an object with items as property for the given array as start value for reducing.
This allows to access the parent object and use items for any further operation.
lastIndex = indices.pop();
parent = indices.reduce((r, index) => r.items[index], { items: myArray });
// further use
parent.items.splice(lastIndex, 1); // delete
Here is a solution using object-scan.
The main advantage of using object-scan is that you get more control to adjust your functions (e.g. if you wanted to get multiple entries or do fuzzy key matching etc). However there is increased complexity, so it's a trade-off and depends on your requirements.
// const objectScan = require('object-scan');
const myArray = [{ id: '0', items: [] }, { id: '1', items: [ { id: '1.0', items: [{ id: '1.0.0' }, { id: '1.0.1' }] }, { id: '1.1', items: [] } ] }];
const indicesToNeedle = (indices) => indices.map((idx) => `[${idx}]`).join('.items');
const get = (array, indices) => objectScan(
[indicesToNeedle(indices)],
{ abort: true, rtn: 'value' }
)(array);
const splice = (array, indices, deleteCount, ...toBeInserted) => objectScan(
[indicesToNeedle(indices)],
{
abort: true,
rtn: 'bool', // returns true iff spliced
filterFn: ({ parent, property }) => {
parent.splice(property, deleteCount, ...toBeInserted);
return true;
}
}
)(array);
console.log(get(myArray, [1, 0, 1]));
// => { id: '1.0.1' }
console.log(get(myArray, [1, 0, 2]));
// => undefined
// removes "1.0.1" item and inserts two objects there
console.log(splice(myArray, [1, 0, 1], 1, { id: '1.0.1-new' }, { id: '1.0.2-new' }));
// => true
console.log(myArray);
// => [ { id: '0', items: [] }, { id: '1', items: [ { id: '1.0', items: [ { id: '1.0.0' }, { id: '1.0.1-new' }, { id: '1.0.2-new' } ] }, { id: '1.1', items: [] } ] } ]
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/object-scan#13.8.0"></script>
Disclaimer: I'm the author of object-scan

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))

Building adjacency matrix from an array of objects

I have a plain JavaScript array of objects, say e.g.
const drawings = [
{
name: "Foo",
category: "widget"
},
{
name: "Bar",
category: "widget"
},
{
name: "Bar",
category: "fidget"
},
]
etc, where both the name and category have duplicates. What I want to end up with is essentially a list of objects (this is to meet the interface for a 3rd party library), where each object represents a name, and then for each category there is a property that is either true or false, depending on the original list. So for the example the output would be:
const output = [
{
name: "Foo",
widget: true,
fidget: false
},
{
{
name: "Bar",
widget: true,
fidget: true
},
]
I would first go through and make an object of your categories with the categories as keys and default values as false.
Then you can assign this to each object and set the correct keys to true as you go through.
const drawings = [{name: "Foo",category: "widget"},{name: "Bar",category: "widget"},{name: "Bar",category: "fidget"},]
// make category object where everything is false
let category_obj = drawings.reduce((a, item) => (a[item.category] = false, a), {})
let output = drawings.reduce((a, {name, category}) => {
// assign cat
if (!a.hasOwnProperty(name)) a[name] = Object.assign({}, {name}, category_obj)
// set to true if the correct category
a[name][category] = true
return a
}, {})
// the above makes an object, but you only want the array of values
console.log(Object.values(output))
If you already know the categories or if you have infered them as you suggested, you could use Array.reduce() like such:
drawings.reduce(function(acc, curr) {
if (!acc.some(elt => elt.name === curr.name)) {
acc.push({name: curr.name, widget: false, fidget: false})
}
const i = acc.findIndex(elt => elt.name === curr.name)
acc[i][curr.category] = true
return acc
}, [])

Categories