I would like to sort the object indexing like array.sort
Input is
"a":{"label":"0",isEnabled":false,"status":1},
"1":{"label":"1",isEnabled":false,"status":1},
"$":{"label":"2",isEnabled":false,"status":1},
"B":{"label":"3",isEnabled":false,"status":1},
"0":{"label":"5",isEnabled":false,"status":1},
"/":{"label":"6",isEnabled":false,"status":1}
expected output
"$":{"label":"2",isEnabled":false,"status":1},
"/":{"label":"6",isEnabled":false,"status":1},
"0":{"label":"5",isEnabled":false,"status":1},
"1":{"label":"1",isEnabled":false,"status":1},
"a":{"label":"0",isEnabled":false,"status":1},
"B":{"label":"3",isEnabled":false,"status":1}
Actual result are, I understand object by default sorting with numbers, but I would like to sort like expected output as above mentioned, Any inputs please?
"0":{"label":"5",isEnabled":false,"status":1},
"1":{"label":"1",isEnabled":false,"status":1},
"$":{"label":"2",isEnabled":false,"status":1},
"/":{"label":"6",isEnabled":false,"status":1},
"a":{"label":"0",isEnabled":false,"status":1},
"B":{"label":"3",isEnabled":false,"status":1}
Ok, object keys sorted and then used for display.
let obj = {
"a": {
"label": "0",
"isEnabled": false,
"status": 1
},
"1": {
"label": "1",
"isEnabled": false,
"status": 1
},
"$": {
"label": "2",
"isEnabled": false,
"status": 1
},
"B": {
"label": "3",
"isEnabled": false,
"status": 1
},
"0": {
"label": "5",
"isEnabled": false,
"status": 1
},
"/": {
"label": "6",
"isEnabled": false,
"status": 1
}
};
// Get keys in sorted order and print.
const arrKeys = Object.getOwnPropertyNames(obj).sort((a, b) => a < b);
arrKeys.forEach(k => console.log(obj[k]));
Since object property order is not guaranteed you next best thing is using Map. With Map the insertion order is honored and you can use it as an index:
The Map object holds key-value pairs and remembers the original
insertion order of the keys.
let obj = { "a":{"label":"0",isEnabled:false,"status":1}, "1":{"label":"1",isEnabled:false,"status":1}, "$":{"label":"2",isEnabled:false,"status":1}, "B":{"label":"3",isEnabled:false,"status":1}, "0":{"label":"5",isEnabled:false,"status":1}, "/":{"label":"6",isEnabled:false,"status":1} }
let regEx = new RegExp(/[^a-zA-Z\d\s:]/)
let sortedKeys = Object.keys(obj).sort((a, b) => {
if (regEx.test(a) && regEx.test(b))
return a.charCodeAt(0) - b.charCodeAt(0)
else
return a.localeCompare(b, 'en', { numeric: true, caseFirst: 'upper'})
})
let mapped = sortedKeys.reduce((r,c) => (r.set(c, obj[c]), r), new Map())
console.log('keys: ', Array.from(mapped.keys()))
console.log('values: ', Array.from(mapped.values()))
Related
So I am trying to set up a filter on nested array of objects. The thing is the filter is object.
I just got the empty result.
Data sample:
obj:[
{
"success": true,
"data": {
"purpose": {
"label": "Purpose",
"type": "choice",
"value": {
"select": "loremIpsum",
"options": [
{
"value": "loremIpsum",
"text": "loremIpsum"
}
]
},
"filter_groups": [
"sd",
"coll"
],
"checklist_groups": {
"sd": {
"value": null,
"comment": [],
"is_disabled": false
}
}
},
"description": {
"label": "Description",
"type": "choice",
"value": {
"select": "loremIpsum",
"options": [
{
"value": "loremIpsum",
"text": "loremIpsum"
}
]
},
"filter_groups": [
"dv"
],
"checklist_groups": {
"sd": {
"value": null,
"comment": [],
"is_disabled": false
}
}
}
}
}
]
Try to do next:
const filterData = (e) => {
let filtredData = Object.entries(obj)
.map(item => ({
...item,
filter_groups: item.filter_groups
.filter(child => child === e)
}))
return filtredData;
}
But filter wants work, how filter by nested objects?
I want to filter by filter_groups.
Do I have to use a regex to define the filter?
It's really not entirely clear what exactly you want as input and how your output should be formatted. This version assumes you just want the data property of the first value of obj's array, and turns it back into an object with the same sorts of keys you had in the original (such as "purpose" or "description").
const filterData = (e) => (data) => Object .fromEntries (
Object .entries (data) .filter (([k, {filter_groups}]) => filter_groups .includes (e))
)
const obj = [{success: !0, data: {purpose: {label: "Purpose", type: "choice", value: {select: "loremIpsum", options: [{value: "loremIpsum", text: "loremIpsum"}]}, filter_groups: ["sd", "coll"], checklist_groups: {sd: {value: null, comment: [], is_disabled: !1}}}, description: {label: "Description", type: "choice", value: {select: "loremIpsum", options: [{value: "loremIpsum", text: "loremIpsum"}]}, filter_groups: ["dv"], checklist_groups: {sd: {value: null, comment: [], is_disabled: !1}}}}}]
console .log ('"sd": ', filterData ('sd') (obj [0] .data))
console .log ('"dv": ', filterData ('dv') (obj [0] .data))
console .log ('"foobar": ', filterData ('foobar') (obj [0] .data))
.as-console-wrapper {max-height: 100% !important; top: 0}
Forgetting that outer shell, the trick here is that we convert it to an array of key-value entries with Object .entries, filter those entries by whether the filter_groups property of the value contains our target value, and convert the resulting array of entries back into an object.
Try using a predicate method to perform a boolean test and determine it obj:[] is behaving live an array
```Array.isArray(arr); // true ```
How can I know if in a JSON exists "xxx" key? I need these JSONs to be formatted:
let beforeOne = {
"id": "123",
"aDate": {
"$date": "2022-06-24T00:00:00Z"
}
}
let beforeTwo = {
"id": "123",
"firstDate": {
"$date": "2022-06-24T00:00:00Z"
},
"day": {
"today": {
"$date": "2022-06-24T00:00:00Z"
},
"tomorrow": {
"$date": "2022-06-24T00:00:00Z"
}
}
}
to:
let afterOne = {
"id": "123",
"aDate": new Date("2022-06-24T00:00:00Z")
}
let afterTwo = {
"id": "123",
"firstDate": new Date("2022-06-24T00:00:00Z"),
"day": {
"today": new Date("2022-06-24T00:00:00Z"),
"tomorrow": new Date("2022-06-24T00:00:00Z")
}
}
So basically, I need to find everywhere where "$date" is present, remove it and give the parentKey the value from parentKey.$date with the new Date() constructor. How could I do that? Thanks in advance!
You can use a recursive function for this. Each time you see an object that has a $date key, perform the new Date transformation and return this to the caller:
function transformDates(obj) {
return Object(obj) !== obj ? obj // Primitive
// When it has $date:
: obj.hasOwnProperty("$date") ? new Date(obj.$date)
// Recursion
: Object.fromEntries(
Object.entries(obj).map(([k, v]) => [k, transformDates(v)])
);
}
let beforeOne = {"id": "123","aDate": {"$date": "2022-06-24T00:00:00Z"}}
console.log(transformDates(beforeOne));
let beforeTwo = {"id": "123","firstDate": {"$date": "2022-06-24T00:00:00Z"},"day": {"today": {"$date": "2022-06-24T00:00:00Z"},"tomorrow": {"$date": "2022-06-24T00:00:00Z"}}}
console.log(transformDates(beforeTwo));
Note that Stack Snippets converts Date objects back to string when outputting them. This is not what you would see in a browser's console, where you really get a rendering of Date objects.
I have this array
const d = [{
"type": "price",
"value": {
"min": 0,
"max": 170
}
}, {
"type": "name",
"value": {}
}, {
"type": "volume",
"options": [1,2]
}]
I want to filter if value doesn't have a value or options is an empty array. So I did
d.filter(o => o.value || o.options)
I expect type:name is gone but why it's still there?
I also tried lodash
d.filter(o => !isEmpty(o.value) || !isEmpty(o.options))
doesn't work as expected?
{} and [] are truthy values
console.log(Boolean([]))
console.log(Boolean({}))
You can check for length in case of array, in case of object you can get keys or values or entries and check it's length
const d = [{"type": "price","value": {"min": 0,"max": 170}}, {"type": "name","value": {}}, {"type": "volume","options": [1,2]}]
let op = d.filter(o => ( Object.values(o.value || {}) || (o.options || [] )).length)
console.log(op)
With lodash, you can use _.reject() with _.isEmpty() to remove items that have an empty value object and an empty options array:
const arr = [{"type": "price","value": {"min": 0,"max": 170}}, {"type": "name","value": {}}, {"type": "volume","options": [1,2]}]
const result = _.reject(arr, o => _.isEmpty(o.value) && _.isEmpty(o.options))
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js"></script>
With lodash/fp you can easily generate a function that extracts the value and the options, iterates them with _.every() and return true (should be removed) if both are empty:
const { reject, flow, props, every, isEmpty } = _
const fn = reject(flow(props(['value', 'options']), every(isEmpty)))
const arr = [{"type": "price","value": {"min": 0,"max": 170}}, {"type": "name","value": {}}, {"type": "volume","options": [1,2]}]
const result = fn(arr)
console.log(result)
<script src='https://cdn.jsdelivr.net/g/lodash#4(lodash.min.js+lodash.fp.min.js)'></script>
Here is my solution.
const d = [{
"type": "price",
"value": {
"min": 0,
"max": 170
}
}, {
"type": "name",
"value": {}
}, {
"type": "volume",
"options": [1,2]
}]
function notEmpty(n){
return (!!n ? typeof n === 'object' ? Array.isArray(n) ? !!n.length : !!Object.keys(n).length : true : false);
}
console.log(d.filter(o => notEmpty(o.value) || notEmpty(o.options)));
Hope will help you.
Give the custom filter callback where you check for any entry in values property and options property.
const d = [
{
"type": "price",
"value": {
"min": 0,
"max": 170
}
},
{
"type": "name",
"value": {}
},
{
"type": "volume",
"options": [1,2]
}
];
function doesHaveValuesAndOptions(obj) {
const valueEntries = obj.value ? Object.entries(obj.value) : [],
options = obj.options || [];
if (!valueEntries.length && !options.length)
return false;
else if (valueEntries.length || options.length)
return true;
return false;
}
const res = d.filter(doesHaveValuesAndOptions);
console.log(res);
I have an array from an API call.
var response = {
"data": {
"data": [{
"1": "Arun",
"index": "name"
}, {
"1": 70.78,
"index": "score"
}]
}
}
I connect to a lot of other API's and they return me a similar response but the keys change. Sometimes it might be
var response = {
"data": {
"data": [{
"values": "Harry",
"index": "name"
}, {
"values": 45,
"index": "score"
}]
}
}
var response = {
"data": {
"data": [{
"4": "Richard",
"index": "name"
}, {
"4": 98,
"index": "score"
}]
}
}
I would like to get an array like this.
[
{
name: 'Arun',
score: 70.78
}
]
This is what I did.
var response = {
"data": {
"data": [{
"1": "Arun",
"index": "name"
}, {
"1": 70.78,
"index": "score"
}]
}
}
const result = [];
const mappedData = _.map(response.data.data, (item) => {
return {
[item.index]: item[1]
};
});
const resultObject = _.reduce(mappedData, (result, currentObject) => {
for (const key in currentObject) {
if (currentObject.hasOwnProperty(key)) {
result[key] = currentObject[key];
}
}
return result;
}, {});
result.push(resultObject)
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
So instead of hardcoding "1" or "values" in the map function, is there a more universal way to get the key and achieve the same result?
Thanks.
Use reduce rather than map, so you're updating the same object, not creating an array.
And since the property containing the value can vary, I use a loop to look for the first property that isn't named index, and use its value.
var response = {
"data": {
"data": [{
"1": "Arun",
"index": "name"
}, {
"1": 70.78,
"index": "score"
}]
}
}
const mappedData = response.data.data.reduce((acc, item) => {
var value;
// find the property that isn't named "item"
for (var i in item) {
if (i != "index") {
value = item[i];
break;
}
}
acc[item.index] = value;
return acc;
}, {});
console.log(mappedData)
There's no need for lodash for this, the built-in reduce function is fine (but _.reduce will work similarly).
Since you only care about the values of that object and it only has two keys you can do this quite easily in lodash with reduce & fromPairs:
var response = { "data": { "data": [{ "1": "Arun", "index": "name" }, { "1": 70.78, "index": "score" }] } }
const rv = (o) => _.reverse(_.values(o))
const r = _.reduce(response.data.data, (a,c) => _.fromPairs([rv(a), rv(c)]))
console.log(r)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script>
The same thing converted to ES6 would be:
var response = { "data": { "data": [{ "1": "Arun", "index": "name" }, { "1": 70.78, "index": "score" }] } }
const rv = (o) => Object.values(o).reverse() // reverse values
const fp = (arr) => arr.reduce((r, [k,v]) => (r[k] = v, r), {}) // from pairs
const result = response.data.data.reduce((a,c) => fp([rv(a), rv(c)]))
console.log(result)
The main idea here is to first get the object values in an array form, reverse them so the key & value are in the correct order and then reduce that array via from pairs to create the final object.
The main advantage of this approach is that we never deal with the object keys and only focus on the values which is what you really care about. This way the keys can be any value and it would still not matter.
You could try deleting the key-pair index and using the first value of the resulting object:
const mappedData = _.map(response.data.data, (item) => {
var tempObj = Object.assign({}, item)
var index = tempObj.index;
delete tempObj.index;
var otherData = Object.values(tempObj)[0];
return {
[index]: otherData
};
});
Just modified the #barmar approach. I have used Object.keys to get keys from object. This will remove the any hard-coded dependency.
var response = {
"data": {
"data": [{
"1": "Arun",
"index": "name"
}, {
"1": 70.78,
"index": "score"
}]
}
}
const mappedData = response.data.data.reduce((acc, item,i) => {
var key = Object.keys(item);
acc[item[key[1]]] = item[key[0]]
return acc ;
}, {});
console.log(mappedData)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
I have some JSON like this:
{
"a": { "text": "text", "index": 5 },
"b": { "text": "text", "index": 3 },
"c": { "text": "text", "index": 1 },
}
Now I need to interate this object and call a function on every property of the first level (a, b and c), but I have to do it on order using the "index" property, like "c" first, then "b" and last "a".
However I read that I shouldn't use a for in loop:
A for...in loop iterates over the properties of an object in an arbitrary order (see the delete operator for more on why one cannot depend on the seeming orderliness of iteration, at least in a cross-browser setting).
then how I can do this?
Thanks
You could,
Get the properties of the object as an array using Object.keys().
Sort the properties of the object using sort().
Use forEach() to iterate through the sorted items (which is executed in ascending order of the array).
var items = {
"a": {
"text": "text",
"index": 5
},
"b": {
"text": "text",
"index": 3
},
"c": {
"text": "text",
"index": 1,
}
};
Object.keys(items).sort(function(a, b) {
return items[a].index - items[b].index;
}).forEach(doStuff);
function doStuff(key) {
console.log(items[key]);
}
You can use getOwnPropertyNames
let obj = {
"a": { "text": "text", "index": 5 },
"b": { "text": "text", "index": 3 },
"c": { "text": "text", "index": 1 }
};
function test(p) {
console.log(p);
}
Object.getOwnPropertyNames(obj)
.reverse()
.forEach(function(p){
test(obj[p]);
});
You can try the following:
Covert the object to an array with elements in sorted order based on the index.
Than simply forEach() on the sorted properties.
Sort function can be implemented as :
var obj = {
"a": { "text": "text", "index": 5 },
"b": { "text": "text", "index": 3 },
"c": { "text": "text", "index": 1},
}
function sortProperties(obj, sortedBy, isNumericSort, reverse) {
sortedBy = sortedBy || 1; // by default first key
isNumericSort = isNumericSort || false; // by default text sort
reverse = reverse || false; // by default no reverse
var reversed = (reverse) ? -1 : 1;
var sortable = [];
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
sortable.push([key, obj[key]]);
}
}
if (isNumericSort)
sortable.sort(function (a, b) {
return reversed * (a[1][sortedBy] - b[1][sortedBy]);
});
else
sortable.sort(function (a, b) {
var x = a[1][sortedBy].toLowerCase(),
y = b[1][sortedBy].toLowerCase();
return x < y ? reversed * -1 : x > y ? reversed : 0;
});
return sortable; // array in format [ [ key1, val1 ], [ key2, val2 ], ... ]
}
sortProperties(obj, 'index', true, false);
The another solution is store the object keys and reverse it, then iterate with the object keys.
var obj = {
"a": { "text": "text", "index": 5 },
"b": { "text": "text", "index": 3 },
"c": { "text": "text", "index": 1 }
};
var yourFunction = console.log;
var keys = Object.keys(obj);
keys.reverse();
for (var i = 0; i < keys.length; i++) {
yourFunction(obj[keys[i]]);
}
you can also make use of reverse() method of javascript if you want to access the json items in reverse order.
get the array of all the keys of json using Object.keys()
reverse the array using reverse() method
and finally you can make use of javascript forEach() method to work on the items of the json. Sample code is shown below
let obj = {
"a": { "text": "text", "index": 5 },
"b": { "text": "text", "index": 3 },
"c": { "text": "text", "index": 1 },
}
Object.keys(obj)
.reverse()
.forEach(ele => {
console.log(obj[ele]);
});
visit This link to know more about reverse() method
Probably you should use Object.keys to get a list of all the properties, sort that list, then iterate over it.