javascript: sorting an array of objects derives inconsistent results - javascript

I am working to sort an array of objects in descending order using pure javascript. I initially thought that a JSON.stringify method was causing the issue, as described [here][1]. Apparently JSON.stringify does not preserve the order of objects by design. Give the below array of objects I seem to get a random sort order using the following code where I sort the array of objects and then derive the weight property of the first object in the array.
var byWeight = objArray.slice(0);
var sorted = byWeight.sort(function(a,b) { return b.weight - a.weight; } );
sorted[0].something.weight;
On distinct runs of this code, I seem to randomly derive .29 or .35 for the weight. Why is this occurring?
Here is the array:
[
{
"something": {
"terms": [
{
"elements": {
"number": {
"span": [
9,
10
],
"value": "1"
}
},
"span": [
9,
12
],
"value": "1g",
"label": "grams"
}
],
"span": [
9,
12
],
"weight": 0.29,
"value": "1gm"
}
},
{
"something": {
"terms": [
{
"elements": {
"number": {
"span": [
16,
18
],
"value": "30"
}
},
"span": [
16,
20
],
"value": "30ml",
"label": "liters"
}
],
"span": [
16,
20
],
"weight": 0.35,
"value": "30ml"
}
}
]

You have the wrong reference. Change this
var sorted = byWeight.sort(function(a,b) { return b.weight - a.weight; } );
to (watch .something.)
byWeight.sort(function (a, b) { return b.something.weight - a.something.weight; });
Working model:
var data = [
{
"something": {
"terms": {
"span": [
9,
12
],
"value": "1g",
"label": "grams"
}
,
"span": [
9,
12
],
"weight": 0.29,
"value": "1gm"
}
},
{
"something": {
"terms": {
"span": [
16,
20
],
"value": "30ml",
"label": "liters"
}
,
"span": [
16,
20
],
"weight": 0.35,
"value": "30ml"
}
}
];
var sorted = data.slice(0);
sorted.sort(function (a, b) {
return b.something.weight - a.something.weight; // desc!
});
document.write('<pre>' + JSON.stringify(sorted, 0, 4) + '</pre>');

Your objArray is poorly formatted, "terms" shouldn't be an array if it contains key-indexed elements like "span", "value", and "label' it should be an object; then your sort is looking in the wrong place, it should be looking at a.something.weight/b.something.weight instead. jsfiddle.net/3qgtn9r1
var objArray = [
{
"something": {
"terms": {
"span": [
9,
12
],
"value": "1g",
"label": "grams"
},
"span": [
9,
12
],
"weight": 0.29,
"value": "1gm"
}
},
{
"something": {
"terms": {
"span": [
16,
20
],
"value": "30ml",
"label": "liters"
},
"span": [
16,
20
],
"weight": 0.35,
"value": "30ml"
}
}
];
var byWeight = objArray.slice(0);
var sorted = byWeight.sort(function(a,b) { return a.something.weight - b.something.weight; } );
console.log(sorted[0].something.weight);

Related

how to change the format of json array by loping over

Hi I am getting data from API but I want my data in different format so that I can pass later into a function. I want to change the names of keys into a different one becasuse I have created a chart and it only draws if I send it data in certain way
This is what I am getting from API
data = {
"status": "success",
"from": "DB",
"indice": "KSE100",
"data": [
{
"stock_sector_name": "Tc",
"sector_score": "0828",
"stocks": [
{
"stock_symbol": "TRG",
"stock_score": 44
},
{
"stock_symbol": "SYS",
"stock_score": 33
}
]
},
{
"stock_sector_name": "OIL",
"sector_score": "0828",
"stocks": [
{
"stock_symbol": "FFS",
"stock_score": 44
},
{
"stock_symbol": "SMS",
"stock_score": 33
}
]
},
]
}
But I want my data to look like this like this
data = {
"name": "KSE100",
"children": [
{
"name": "A",
'points': -9,
"children": [
{
"stock_title": "A",
"value": 12,
},
{
"stock_title": "B",
"value": 4,
},
]
},
{
"name": "B",
'points': 20,
"children": [
{
"stock_title": "A",
"value": 12,
},
{
"name": "B",
"value": 4,
},
]
},
]
}
Like I want to replace
stock_sector_name = name
sector_score = value
stocks = children
stock_symbol = name
stock_score = value
I have been trying this for so much time but sill could not figured it out
function convert(d){
return {
name : d.indice,
children : d.data.map(y=>{
return {
name : y.stock_sector_name,
points : y.sector_score,
children : y.stocks.map(z=>{
return {
stock_title: z.stock_symbol,
value : z.stock_score
}
})
}
})
}
}
You can do something like this
const data = {
"status": "success",
"from": "DB",
"indice": "KSE100",
"data": [{
"stock_sector_name": "Tc",
"sector_score": "0828",
"stocks": [{
"stock_symbol": "TRG",
"stock_score": 44
},
{
"stock_symbol": "SYS",
"stock_score": 33
}
]
},
{
"stock_sector_name": "OIL",
"sector_score": "0828",
"stocks": [{
"stock_symbol": "FFS",
"stock_score": 44
},
{
"stock_symbol": "SMS",
"stock_score": 33
}
]
},
]
}
const data2 = {
"name": "KSE100",
"children": [{
"name": "A",
'points': -9,
"children": [{
"stock_title": "A",
"value": 12,
},
{
"stock_title": "B",
"value": 4,
},
]
},
{
"name": "B",
'points': 20,
"children": [{
"stock_title": "A",
"value": 12,
},
{
"name": "B",
"value": 4,
},
]
},
]
}
//stock_sector_name = name
//sector_score = value
//stocks = children
//stock_symbol = stock_title
//stock_score = value
const keys = {
stock_sector_name: "name",
sector_score: "points",
stocks: "children",
stock_symbol: "stock_title",
stock_score: "value",
indice: "name",
//data: "children"
}
const rename = (value) => {
if (!value || typeof value !== 'object') return value;
if (Array.isArray(value)) return value.map(rename);
return Object.fromEntries(Object
.entries(value)
.map(([k, v]) => [keys[k] || k, rename(v)])
);
}
renamedObj = rename(data);
console.log(renamedObj);

Combining objects with duplicate values inside an array

If I have an array of objects and there are certain objects that have the same name/property (in my case there are 2 instances of objects with a name of "highway"), how can I remove these objects from the array and add a new object with that contains data from both?
const data = [
{ "data": [ { "x": "sensor_error_code", "y": [ 0, 9 ] } ], "name": 123 },
{ "data": [ { "x": "road_type", "y": [ 15, 24 ] } ], "name": "city" },
{ "data": [ { "x": "road_type", "y": [ 0, 14 ] } ], "name": "highway" },
{ "data": [ { "x": "road_type", "y": [ 25, 30 ] } ], "name": "highway" },
{ "data": [ { "x": "weather", "y": [ 0, 8 ] } ], "name": "rain" },
{ "data": [ { "x": "weather", "y": [ 9, 24 ] } ], "name": "sun" },
{ "data": [ { "x": "special_object", "y": [ 5, 15 ] } ], "name": true }
];
e.g. having one instance of "highway" with a data object containing the values from both:
{ "data": [ { "x": "road_type", "y": [ 0, 14 ] }, { "x": "road_type", "y": [ 25, 30 ] } ], "name": "highway" }
So far I've tried to make a new empty object and loop over the data array, accumulating that empty object with names and values from that array. I was planning on converting that object to an array in the end. The code I've used is below:
const myData = {};
atts.forEach(att => {
if (myData[att.name]) {
myData[att.name].push(att.data)
} else {
myData[att.name] = att.data;
}
})
console.log(myData)
However, the code I've used above involves some additional manipulation to make the data ready for consumption by a library and I was wondering if there are simpler ways to achieve my goal?
Your solution has a decent start. There are only 2 problems with your solution:
You need to "unwrap" the array when pushing it
You need to combine them all together at the end
const myData = {};
atts.forEach(att => {
if (myData[att.name]) {
myData[att.name].push(...att.data) // Notice this change
} else {
myData[att.name] = att.data;
// or if you don't want to alter the original data
myData[att.name] = [...att.data]; // basically a shallow copy
}
})
const result = [];
for (const key in myData) {
result.push({ data: myData[key], name: key });
}
console.log(result);
You can use .reduce() (to convert your array into an aggregated value) along with .findIndex() (this helps in finding if data from same category exists in the new array you are making)
const atts = [
{ "data": [ { "x": "sensor_error_code", "y": [ 0, 9 ] } ], "name": 123 },
{ "data": [ { "x": "road_type", "y": [ 15, 24 ] } ], "name": "city" },
{ "data": [ { "x": "road_type", "y": [ 0, 14 ] } ], "name": "highway" },
{ "data": [ { "x": "road_type", "y": [ 25, 30 ] } ], "name": "highway" },
{ "data": [ { "x": "weather", "y": [ 0, 8 ] } ], "name": "rain" },
{ "data": [ { "x": "weather", "y": [ 9, 24 ] } ], "name": "sun" },
{ "data": [ { "x": "special_object", "y": [ 5, 15 ] } ], "name": true }
];
let myData = atts.reduce((agg, x) => {
let isIndex = agg.findIndex(a => a.name == x.name);
if(isIndex > -1) agg[isIndex].data.push(x.data[0]);
else {
agg.push({data : x.data , name : x.name});
}
return agg;
},[]);
console.log(myData)
A different approach using reduce - tracking the items by keys and merging the arrays of duplicate objects
const combined = Object.entries(data.reduce((b,a) => {
// our result will be an object so wrap it in Object.entries so we can iterate through to finish
if (b.hasOwnProperty(a.name)) b[a.name].data = [...b[a.name].data, ...a.data];
// if the property exists, merge the 'y' array
else b[a.name]=a;
// otherwise add to the object
return b;
},{})).map(e => e[1])
// finally map the result out to the desired format
const data = [
{ "data": [ { "x": "sensor_error_code", "y": [ 0, 9 ] } ], "name": 123 },
{ "data": [ { "x": "road_type", "y": [ 15, 24 ] } ], "name": "city" },
{ "data": [ { "x": "road_type", "y": [ 0, 14 ] } ], "name": "highway" },
{ "data": [ { "x": "road_type", "y": [ 25, 30 ] } ], "name": "highway" },
{ "data": [ { "x": "weather", "y": [ 0, 8 ] } ], "name": "rain" },
{ "data": [ { "x": "weather", "y": [ 9, 24 ] } ], "name": "sun" },
{ "data": [ { "x": "special_object", "y": [ 5, 15 ] } ], "name": true }
];
const combined = Object.entries(data.reduce((b,a) => {
if (b.hasOwnProperty(a.name)) b[a.name].data[0].y = [...b[a.name].data[0].y, ...a.data[0].y];
else b[a.name]=a;
return b;
},{})).map(e => e[1])
console.log(combined)

Count key values from a nested object array

I have an array like this:
[
{
"costs": [{
"value": "80"
}],
"id": 4,
"name": "Subscription Fee",
"month": "March"
},
[
{
"costs": [{
"value": "200"
}],
"id": 2,
"name": "Tution",
"month": "March"
},
{
"costs": [{
"value": "10"
}],
"id": 11,
"name": "DEMO"
}
]
]
I need to have sumation of all the values from costs. How can i do that?
const data = [
{"costs":[{"value":"80"}],"id":4,"name":"Subscription Fee","month":"March"},
[
{"costs":[{"value":"200"}],"id":2,"name":"Tution","month":"March"},
{"costs":[{"value":"10"}],"id":11,"name":"DEMO"}
]
];
// flatten the arrays to get a list of objects
// iterate over this list
const res = data.flat().reduce((total, { costs = [] }) => {
// add the values of this item's costs with total
costs.forEach(({ value = 0 }) => total += +value);
return total;
}, 0);
console.log(res);

Look up values in an array using looping forEach Google Apps Script Javascript

I have an object that looks like the following {key: id numbers}
var obj = {
"c4ecb": {id: [3]},
"a4269": {id: [34,36]},
"d76fa": {id: [54,55,60,61]},
"58cb5": {id: [67]}
}
How do I loop each above id in the following array, and return the label?
var response =
{
"success": true,
"data": [
{
"key": "c4ecb",
"name": "fruits",
"options": [
{
"label": "strawberry",
"id": 3
},
{
"label": "apple",
"id": 4
},
{
"label": "pineapple",
"id": 5
},
{
"label": "Other",
"id": 31
}
],
}
]
},
{
"success": true,
"data": [
{
"key": "a4269",
"name": "vegetables",
"options": [
{
"label": "lettuce",
"id": 34
},
{
"label": "cucumber",
"id": 35
},
{
"label": "radish",
"id": 36
}
],
}
]
},
{
"success": true,
"data": [
{
"key": "d76fa",
"name": "pasta",
"options": [
{
"label": "spaghetti",
"id": 54
},
{
"label": "rigatoni",
"id": 55
},
{
"label": "linguine",
"id": 56
},
{
"label": "lasagna",
"id": 60
},
{
"label": "fettuccine",
"id": 61
}
],
}
]
}
Finally, what I want to do is look up the key and return a string of id values.
For example, input c4ecb and output strawberry. Input a4269 and output lettuce, radish. Input d76fa and output "spaghetti, rigatoni, lasagna, fettuccine"
I think to join the multiple labels output into one string I could use something like
array.data.vegetables.map(vegetables => vegetables.value).join(', ')].toString();
So in the end I want to have something like
var fruits = [some code that outputs "strawberry"];
var vegetables = [some code that outputs "lettuce, radish"];
var pasta = [some code that outputs "spaghetti, rigatoni, lasagna, fettuccine"];
What I've tried so far:
The following loop will return the id only if there is one id to be called for: e.g. only in case one where {id: 3} but returns null in cases like {id: 34,36} (because it's looking for '34,36' in id, which doesn't exist - I need to look for each one individually.
response.data.forEach(({key, options}) => {
if (obj[key]) {
options.forEach(({id, label}) => {
if (id == obj[key].id) obj[key].label = label;
});
}
});
console.log(obj)
Filter the response object to focus on the category that matches the id.
Map over the options array and select the items which appear in obj[id].
Finally convert the filtered results to a string.
See filteredLabelsAsString() function below for implementation.
var obj = {
"c4ecb": {"id": [3]},
"a4269": {"id": [34,36]},
"d76fa": {"id": [54,55,60,61]},
"58cb5": {"id": [67]}
}
var response =
[{
"success": true,
"data": [
{
"key": "c4ecb",
"name": "fruits",
"options": [
{
"label": "strawberry",
"id": 3
},
{
"label": "apple",
"id": 4
},
{
"label": "pineapple",
"id": 5
},
{
"label": "Other",
"id": 31
}
],
}
]
},
{
"success": true,
"data": [
{
"key": "a4269",
"name": "vegetables",
"options": [
{
"label": "lettuce",
"id": 34
},
{
"label": "cucumber",
"id": 35
},
{
"label": "radish",
"id": 36
}
],
}
]
},
{
"success": true,
"data": [
{
"key": "d76fa",
"name": "pasta",
"options": [
{
"label": "spaghetti",
"id": 54
},
{
"label": "rigatoni",
"id": 55
},
{
"label": "linguine",
"id": 56
},
{
"label": "lasagna",
"id": 60
},
{
"label": "fettuccine",
"id": 61
}
],
}
]
}];
function filteredLabelsAsString(obj_key, obj, content=response) {
// sanity check: obj must contain obj_key
if (Object.keys(obj).includes(obj_key)) {
return content.filter((item) => {
// filter content using value of obj_key
return item.data[0].key == obj_key;
}).map((item) => {
// item : { success: true, data: [] }
// map over options array
return item.data[0].options.map((opt) => {
// option : {id, label}
// return the label if the id is in the obj object's list
if (obj[item.data[0].key].id.includes(opt.id))
return opt.label;
}).filter((label) => {
// filter out empty items
return label !== undefined;
});
}).join(",");
}
// if obj does not contain obj_key return empty string
return "";
}
console.log("fruits: " + filteredLabelsAsString("c4ecb", obj));
console.log("vegetables: " + filteredLabelsAsString("a4269", obj));
console.log("pasta: " + filteredLabelsAsString("d76fa", obj));

Compare two arrays of objects and filter results

Example:
const depositAddress = '2NBXPR5PRtW8xBRuDnWXBDXqHYpDPupWnhG';
DBarray1.forEach( (tx) => {
console.log(tx);
})
TXarray2.forEach( (sim) => {
console.log(sim);
});
DBarray1 = [
{
"_id": "575e2b7875a402111900ba8f",
"username": "aleluia#gmail.com",
"playerWallet": "2NFt8YfydBU5JD9U8Xq2ucbfUp2sP7BjUrh",
"User_Profile": {
"TXHash": [
"7fbe28f75412f19dfd123a08ce03c33c302aa13d1e68d38ab8cb4c7418777f8e"
]
}
},
{
"_id": "575e2946b909906a17ea65b9",
"username": "super#user.com",
"playerWallet": "2MzppxEX7xMidjhoJGczFDYsHk5TQwFkjS3",
"User_Profile": {
"TXHash": [
"cf948340a40d3302303dfb3710cfce37bb1cd156dcb6c74561fdc71c0a8fc30b",
"6219def49d2e8284a6031f4c7e05e21adf756d38904e6359bd7844ae14c75a50"
]
}
}
] // end console.log(DBarray1);
TXarray2 = [
{
"id": "cf948340a40d3302303dfb3710cfce37bb1cd156dcb6c74561fdc71c0a8fc30b",
"normalizedHash": "f62af1a61c7eb569c1a171ad23c70bc218bd7244c9c5c92cf7d98638314fbbc5",
"date": "2016-06-21T04:11:18.541Z",
"fee": 6280,
"inputs": [
{
"previousHash": "2660fb761354671912b0cea6427e9ee91a98a507e5f1408865a6058b566b508c",
"previousOutputIndex": 0
},
{
"previousHash": "ce3ef138c11ea4d1766cce52ccf5f1e91790bc03b56561b0eb669041bae4e1a3",
"previousOutputIndex": 0
}
],
"outputs": [
{
"vout": 0,
"account": "2N92kApgroS6CTVuTajtjWtpcAZpUiyQoDT",
"value": 861003
},
{
"vout": 1,
"account": "2NBXPR5PRtW8xBRuDnWXBDXqHYpDPupWnhG",
"value": 3100000,
"isMine": true,
"chain": 0,
"chainIndex": 0
}
],
"entries": [
{
"account": "2MzppxEX7xMidjhoJGczFDYsHk5TQwFkjS3",
"value": -3967283
},
{
"account": "2N92kApgroS6CTVuTajtjWtpcAZpUiyQoDT",
"value": 861003
},
{
"account": "2NBXPR5PRtW8xBRuDnWXBDXqHYpDPupWnhG",
"value": 3100000
}
],
"confirmations": 70,
"pending": false,
"instant": true,
"instantId": "5768be65427689eb06e597559c7e6cf0",
"blockhash": "00000000002d9fb51c7c3c1607fe062eff686aa6be657a59fee6c3044963897d",
"height": 872152
},
{
"id": "6219def49d2e8284a6031f4c7e05e21adf756d38904e6359bd7844ae14c75a50",
"normalizedHash": "179a4466fdfc5470e99e43aa177d43aa4f09e3a06760fd5bebffdda080d4407f",
"date": "2016-06-21T04:13:23.650Z",
"fee": 9096,
"inputs": [
{
"previousHash": "5d2879a79ea3d0dcb50049ef9ca46ef7e8d82caf2073a299a6cd0332add404c8",
"previousOutputIndex": 1
},
{
"previousHash": "d75288e69a3fc2edd534ddcd845af6a280a27af58013ae82828c8a8f813829c1",
"previousOutputIndex": 0
},
{
"previousHash": "eea4f9b274708b60c1b030203543a155857bc54aa11055ada04aceee706f96b9",
"previousOutputIndex": 0
}
],
"outputs": [
{
"vout": 0,
"account": "2NBXPR5PRtW8xBRuDnWXBDXqHYpDPupWnhG",
"value": 2000000,
"isMine": true,
"chain": 0,
"chainIndex": 0
},
{
"vout": 1,
"account": "2MzFTm5jnCDiAapjNnyVgZAJrXMKfQ74esV",
"value": 9859
}
],
"entries": [
{
"account": "2MzcwVFKF274bMNT5tNEDY7Ua7bAgvFUdu9",
"value": -35316
},
{
"account": "2MzFTm5jnCDiAapjNnyVgZAJrXMKfQ74esV",
"value": 9859
},
{
"account": "2MzppxEX7xMidjhoJGczFDYsHk5TQwFkjS3",
"value": -1983639
},
{
"account": "2NBXPR5PRtW8xBRuDnWXBDXqHYpDPupWnhG",
"value": 2000000
}
],
"confirmations": 70,
"pending": false,
"instant": true,
"instantId": "5768bee2b5bdf3f406e7db035aef016a",
"blockhash": "00000000002d9fb51c7c3c1607fe062eff686aa6be657a59fee6c3044963897d",
"height": 872152
},
{
"id": "7fbe28f75412f19dfd123a08ce03c33c302aa13d1e68d38ab8cb4c7418777f8e",
"normalizedHash": "b4f1974dccde5ea9dfb0abcd7d4a6f3f14995d9dd422aa7d2a9078229ff18ff4",
"date": "2016-06-21T03:39:25.034Z",
"fee": 3465,
"inputs": [
{
"previousHash": "97fbb6ed8646f7ce9ed10a4230a70348151d5b6b208ad068e3a1a3fddae2dc0e",
"previousOutputIndex": 2
}
],
"outputs": [
{
"vout": 0,
"account": "2NBXPR5PRtW8xBRuDnWXBDXqHYpDPupWnhG",
"value": 111200000,
"isMine": true,
"chain": 0,
"chainIndex": 0
},
{
"vout": 1,
"account": "2NFJnLrhsCDfG3ooQvGC169gnzBabtRgV2y",
"value": 244246993
}
],
"entries": [
{
"account": "2NCGUnwpNgaJbhMZKLJcBrWvZhWnai5PjVC",
"value": -355450458
},
{
"account": "2NFJnLrhsCDfG3ooQvGC169gnzBabtRgV2y",
"value": 244246993
},
{
"account": "2NBXPR5PRtW8xBRuDnWXBDXqHYpDPupWnhG",
"value": 111200000
}
],
"confirmations": 77,
"pending": false,
"instant": false,
"blockhash": "0000000000509dbc80cc3d86cdb10ce8e87ab7867c6775a9b00ca904fbe70da7",
"height": 872145
}
]// end console.log(TXarray2);
How can we check if TXarray2.id which is the transactions id, if it matches a payment made by the user inside DBarray1.User_Profile.TXHash for examplecf948340a40d3302303dfb3710cfce37bb1cd156dcb6c74561fdc71c0a8fc30b .
I want to know forEach TXarray2.id who made the payment . I tried to do this with promises and i will share some code when i get home but i'm sure it can be done with async for all users one by one and log who made a payment today to this wallet. I tried with array.find() method to check inside TXHash but failed, i don't fully grasp many prototype methods yet...
Hopefully someone already thinks this a walk on the park for him and found a better solution to validate this kind of stuff. I will accept any answer even with lodash, maping, anything. TY !!!
You iterate TXArray2 and do a lookup in DBarray1 for the transactionId.
I like to work with native array methods like map and filter, so I would use something like the following:
const result = TXarray2.map(tx => ({
transaction: tx,
user: DBarray1.filter(user => user.User_Profile.TXHash.indexOf(tx.id) > -1)[0]
}));
In this example result is an array where every element contains a transaction and the matching user.
If you already have all of the transaction and user data, could you do something like this?
// for each transaction in TXarray2
for (let { 'id': transactionId } of TXarray2) {
// check each entry in DBarray1
for (let { '_id': userId, 'User_Profile': { 'TXHash': transactions } } of DBarray1) {
if (transactions.includes(transactionId)) {
console.log(`${transactionId} was made by ${userId}`);
}
}
}
The best way here is to use Array.indexOf (or Array.findIndex if you want to use callbacks) which returns -1 if an entry is not in an array.
Here's the sync variant:
var paid = [];
TXArray2.forEach(function(transaction){
for(var i = 0; i < DBArray1.length; ++i){
if(DBArray1[i].User_Profile.TXHash.indexOf(transaction.id) > -1){
paid.push({
user : DBArray1[i],
transaction : transaction
});
break;
}
}
});
console.log(paid);

Categories