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.
Related
I have a question about how to loop an object in typescript and to get the value that I want.
my object looks like this:
mockdata = {
"subobj1": {
"order": 1
"value": "abc"
},
"subobj2": {
"order": 2
"value": "aaa"
},
...
}
I want to loop this object and only get the subobject, if order == 1
what I expect is to get object
"subobj1": {
"order": 1
"value": "abc"
}
any solutions?
Just loop through the object using for in syntax and find the respective value!
const mockdata = {
"subobj1": {
"order": 1,
"value": "abc"
},
"subobj2": {
"order": 2,
"value": "aaa"
},
}
let output;
for (const property in mockdata) {
if (mockdata[property].order === 1) {
output = {
[property]: mockdata[property]
}
break;
}
}
console.log('result', output);
Here's a pretty slim way to do it. I usually use Object.keys or .entries to all my obj for loops
const arrOfOrderEqualToOne = Object.entries(mockdata).filter(([key, value]) => value.order === 1));
Try this
let mockdata = {
"subobj1": {
"order": 1,
"value": "abc"
},
"subobj2": {
"order": 2,
"value": "aaa"
},
}
let temp;
Object.keys(mockdata).forEach(key=>{
if(mockdata[key].order==1){
temp=mockdata[key]
}
})
console.log(temp)
There is a JSON object:
var items = [{
"item" : "A",
"checked": false,
"info": { "hello": "world" },
"products": []
},
{
"item" : "B",
"checked": true,
"info": { },
"products": [1, 2, 3]
}];
I need to parse each nested object and if there is additional objects, convert them to string.
For example, first object has another object into "info" key. And put the modified JSON Object in a new array.
I have tried like this:
var modifiedObj = [];
items.forEach(function(item) {
//get only values
var val = Object.values(item);
val.forEach(function(el) {
//only if value is an object
if (typeof el === 'object' && !Array.isArray(el)) {
//convert to str
var str = JSON.stringify(el, null, 2);
console.log(str);
}
});
});
It works, but I cannot get how to put the modified JSON in a new array. So my ideal result is:
modifiedObj = [{
"item" : "A",
"checked": false,
"info": "{ 'hello': 'world' }", //String
"products": []
},
{
"item" : "B",
"checked": true,
"info": "{ }", //String
"products": [1, 2, 3]
}];
You can use .map() to iterate over an array and return a new array.
Since the elements of your array are objects, you'll need to use the for...in loop to iterate over the object.
var items = [{
"item": "A",
"checked": false,
"info": {
"hello": "world"
},
"products": []
},
{
"item": "B",
"checked": true,
"info": {},
"products": [1, 2, 3]
}
];
const modObj = items.map(item => {
for (let prop in item) {
if(typeof item[prop] === 'object' && !Array.isArray(item[prop])) {
item[prop] = JSON.stringify(item[prop])
}
}
return item;
})
console.log({ modObj })
PS: I'm not sure which properties you want stringified so I've kept the original if condition in my answer.
Links to read more about for...in and map()
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 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()))
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>