find min from arrays of objects - javascript

I have list of statuses where not started is the minimum , followed by submitted and last is complete.
So if we apply the logic on the data since there are 2 status on the current array of object below which are "Not Started" and "Submitted" so the function should return Not Started because not started is the min.
If there is only 1 item in an array then get the only status as a result.
How do we filter the array of objects and get the min based on the order in statuses array. Thanks for any idea.
#currentCode
getMin(id:number , key:string) {
let data = [
{
"id": 14,
"status": "Submitted",
},
{
"id": 15,
"status": "Not Started",
}
]
let min = Math.min(...data.map(item => item.status));
console.log('result' , min)
}
#order of statuses
statuses: any[] = [
{ viewValue: 'Not Started', value: 1 },
{ viewValue: 'Submitted', value: 2 },
{ viewValue: 'Complete', value: 3 },
]
#sample array of objects - result Not Started
data = [
{
"id": 14,
"status": "Submitted",
},
{
"id": 15,
"status": "Not Started",
}
]
#sample array of objects - result Submitted
data = [
{
"id": 14,
"status": "Submitted",
},
{
"id": 17,
"status": "Complete",
}
]
#sample array of objects - result Complete , since there is only 1 get the only status
data = [
{
"id": 17,
"status": "Complete",
}
]

You need to look up the status from data in your statuses array.
const data = [
{
"id": 14,
"status": "Submitted",
},
{
"id": 17,
"status": "Complete",
}
];
const statuses = [
{ viewValue: 'Not Started', value: 1 },
{ viewValue: 'Submitted', value: 2 },
{ viewValue: 'Complete', value: 3 },
]
let min = Math.min(...data.map(item => statuses.find(st => st.viewValue === item.status).value));
let minStatus = statuses.find(st => st.value === min).viewValue;
console.log(minStatus);

A straightforward solution is to simply sort the array by statuses value, which then gives you access to the complete ordering, and the entire object for each position. Here first mapping the statuses array to an object keyed by viewValue to directly access status values.
const data = [
{ "id": 14, "status": "Submitted", },
{ "id": 17, "status": "Complete", },
{ "id": 15, "status": "Not Started", },
{ "id": 15, "status": null, }
];
const statuses = [
{ viewValue: 'Not Started', value: 1 },
{ viewValue: 'Submitted', value: 2 },
{ viewValue: 'Complete', value: 3 },
];
const statusMap = Object.fromEntries(statuses
.map(({ viewValue, value }) => [viewValue, value])
);
const sorted = data
.filter(d => Object.hasOwn(statusMap, d.status))
.sort((a, b) => statusMap[a.status] - statusMap[b.status]);
const min = sorted.at(0);
const max = sorted.at(-1);
console.log('min:', min.status);
console.log('max:', max.status);
// or the complete objects
console.log({ min, max })
Edit
Added a filter() call before sorting to remove datum that don't have a status that appears in statusMap using Object.hasOwn. This also makes the original spread redundant since filter() returns a new array thus avoiding mutation of the original data array with the sort() call.

Related

Insert new JSON objects in nested JS array based on condition

For one of my e-commerce application requirement, I have a nested array of the form (Sample):
const data = [
{
"id": 1,
"group": "upper-wear",
"labels": [
{
"type": "shirts",
"quantity": "20",
},
],
popular: true
},
{
"id": 2,
"group": "bottom-wear",
"lables": [
{
"type": "trousers",
"quantity": "31",
},
],
popular: true
},
]
To this array, I need to insert new objects to the array 'labels' if the group value equals 'upper-wear'.
const newDataToInsert = [
{
"type": 'blazers',
"quantity": 19
},
]
This is what I tried so far, considering that for now I only need to insert to single label (i.e. 'upper-wear') (in future, there can be multiple labels category 'upper-wear', 'bottom-wear', to be inserted into):
const updatedArray = data.map((datum) => {
if (datum.group === 'upper-wear') {
return {
...datum,
labels: [...datum.labels, ...newDataToInsert]
};
}
});
console.log(updatedArray);
But there seems to be a silly issue that I am missing as the result returns like this:
[
{
id: 1,
group: 'upper-wear',
labels: [ [Object], [Object] ],
popular: true
},
undefined
]
I know there may be better approaches available, but this is what I can think of as the minimum solution for now.
any help to resolve the current or any better solution will be highly appreciated.
Try with this
updatedArray = data.map((d) => {
if (d.group && d.group === 'upper-wear') {
return { ...d, labels: d.labels.concat(newDataToInsert) }
} else {
return d;
}
})
const data = [
{
"id": 1,
"group": "upper-wear",
"labels": [
{
"type": "shirts",
"quantity": "20",
},
],
popular: true
},
{
"id": 2,
"group": "bottom-wear",
"lables": [
{
"type": "trousers",
"quantity": "31",
},
],
popular: true
},
];
const newDataToInsert = [
{
"type": 'blazers',
"quantity": 19
},
];
const updatedArray = data.map((d) => {
if (d.group && d.group === 'upper-wear') {
return { ...d, labels: d.labels.concat(newDataToInsert) }
} else {
return d;
}
});
console.log(updatedArray)
Explaination
Here while mapping the data, we check for the condition
IF
If it matches then we will first copy the whole object from the variable b return { ...b }
after that we take another variable with the same name lables return { ...d, labels: d.labels.concat(newDataToInsert) },As per the JSON default nature the new variable with the same name will hold the latest value
Here in labels we first take a copy of old data and then merge it with newDataToInsert array labels: d.labels.concat(newDataToInsert), It will merge 2 arrays and store them in JSON with the name labels
Else
In else we just return the current values else { return d; }
You don't actually need to iterate with map over the array. Just find an object in the array and change what you want.
const data=[{id:1,group:"upper-wear",labels:[{type:"shirts",quantity:"20"}],popular:true},{id:2,group:"bottom-wear",lables:[{type:"trousers",quantity:"31"}],popular:true}];
const newDataToInsert=[{type:"blazers",quantity:19}];
data.find(({ group }) => group === 'upper-wear')?.labels.push(...newDataToInsert);
console.log(data);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You're not returning all objects from your map. you're only returning a result when your criteria is met. This is resulting in your undefined objects...
const data = [
{ "id": 1, "group": "upper-wear", "labels": [ { "type": "shirts", "quantity": "20", }, ], popular: true },
{ "id": 2, "group": "bottom-wear", "lables": [ { "type": "trousers", "quantity": "31", }, ], popular: true },
]
const newDataToInsert = [ { "type": 'blazers',"quantity": 19 }, ]
const updatedArray = data.map(datum => {
if (datum.group === 'upper-wear') datum.labels = [...datum.labels, ...newDataToInsert]
return datum
});
console.log(updatedArray);
You can use Array#find to locate the desired group and then change labels for the group found. There are two options depending on how many items you would like to insert. Use Array#push to add the desired item; use forEach for more than one item:
const searchgroup = "upper-wear";
const target = data.find(({group}) => group === searchgroup);
target.labels.push(...newDataToInsert); //For one item to insert
//newDataToInsert.forEach(label => target.labels.push( label )); //For more than one item
const data = [{"id": 1, "group": "upper-wear", "labels": [{"type": "shirts", "quantity": "20"},],popular: true }, {"id": 2, "group": "bottom-wear", "lables": [{"type": "trousers", "quantity": "31", },],popular: true}];
const newDataToInsert = [{"type": 'blazers', "quantity": 19}];
//group to find
const searchgroup = "upper-wear";
//target element in data
const target = data.find(({group}) => group === searchgroup);
//check if group was found
if( target ) {
//if there's only one product in newDataToInsert us this:
//target.labels.push(...newDataToInsert);
//if you have more than one product to be inserted use this; also works for one
newDataToInsert.forEach(label => target.labels.push( label ));
} else {
console.log( `No such group found: ${searchgroup}!` );
}
console.log( data );

How to insert elements into specific index in a 2D array in javascript?

I have an object that looks like below
const tableData = [
{
"Location": "London",
"Status": "Unknown"
},
{
"Location": "Delhi",
"Status": "Reachable"
},
{
"Location": "Berlin",
"Status": "Unknown"
},
{
"Location": "Tokyo",
"Status": "Busy"
},
]
Now I want to create a 2D array which will hold this information in a certain way. Here is my code below
const statusOrder = {"Reachable": 0, "Busy": 1, "Unknown": 2}
let statusOrderInfo = Array(Object.keys(statusOrder).length).fill([]);
for(let i=0; i< tableData.length; i++) {
const status = tableData[i]["Status"].trim()
const statusIndex = statusOrder[status]
statusOrderInfo[statusIndex].push(tableData[i])
}
console.log(statusOrderInfo)
As you can see I want each item of the tableData object to be in a certain index of the 2D array. So the item that contains Status as Reachable should be at index 0, the item that contains the Status as Busy should be at index 1 and so on.
So the final output should look like
[
[
{
"Location":"Delhi",
"Status":"Reachable"
}
],
[
{
"Location":"Tokyo",
"Status":"Busy"
}
],
[
{
"Location":"London",
"Status":"Unknown"
},
{
"Location":"Berlin",
"Status":"Unknown"
}
]
]
But I get a wrong output on running the above code even though I am targeting the correct index. What's wrong in my approach?
Using Array#reduce:
const
tableData = [ { "Location": "London", "Status": "Unknown" }, { "Location": "Delhi", "Status": "Reachable" }, { "Location": "Berlin", "Status": "Unknown" }, { "Location": "Tokyo", "Status": "Busy" } ],
statusOrder = {"Reachable": 0, "Busy": 1, "Unknown": 2};
const statusOrderInfo = tableData.reduce((list, e) => {
const index = statusOrder[e.Status];
list[index] = [...(list[index] || []), {...e}];
return list;
}, []);
console.log(statusOrderInfo);
Simple fix on your problem is that just changing your manner to set initial value of statusOrderInfo and use Array.from instead of Array.fill like this:
let statusOrderInfo = Array.from({length: Object.keys(statusOrder).length}, ()=> []);
another solution is set initiali value of statusOrderInfo by empty array, and then in your for loop, after you get the index of current object based on status value, you can check if statusIndex already exist in the statusOrderInfo or not, like this:
const statusOrder = {"Reachable": 0, "Busy": 1, "Unknown": 2}
let statusOrderInfo = [];
for(let i=0; i< tableData.length; i++) {
const status = tableData[i]["Status"].trim()
const statusIndex = statusOrder[status];
if(statusOrderInfo[statusIndex]) statusOrderInfo[statusIndex].push(tableData[i]);
else statusOrderInfo[statusIndex] = [ tableData[i] ]
}
console.log(statusOrderInfo);
another solution, is to use reduce method on array, like this:
const tableData = [{
"Location": "London",
"Status": "Unknown"
},
{
"Location": "Delhi",
"Status": "Reachable"
},
{
"Location": "Berlin",
"Status": "Unknown"
},
{
"Location": "Tokyo",
"Status": "Busy"
},
];
const statusOrder = {"Reachable": 0, "Busy": 1, "Unknown": 2}
const result = tableData.reduce((acc, cur) => {
const index = statusOrder[cur.Status];
if (acc[index]) acc[index].push(cur);
else acc[index] = [cur]
return acc;
}, []);
console.log(result)
Anoter one solution in kind of declartive way:
First of all sort objects by status code using Array#sort
And then just wrap every object to it own array using Array#map
const tableData = [{Location: "London",Status: "Unknown"},{Location: "Delhi",Status: "Reachable"},{Location: "Berlin",Status: "Unknown"},{Location: "Tokyo",Status: "Busy"}]
const statusOrder = {Reachable: 0, Busy: 1, Unknown: 2}
const result = tableData
.sort(({ Status: s1 }, { Status: s2 }) => statusOrder[s1] - statusOrder[s2])
.map((item) => [item]);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
As others explained, reduce would be the best choice for your scenario. Because on every iteration you would create a new Array object.
const tableData = [{ Location: "London", Status: "Unknown" }, { Location: "Delhi", Status: "Reachable" }, { Location: "Berlin", Status: "Unknown" }, { Location: "Tokyo", Status: "Busy" }]
const statusOrder = { "Reachable": 0, "Busy": 1, "Unknown": 2 };
const statusOrderInfo = tableData.reduce((accumulator, currentValue) => {
const index = statusOrder[currentValue.Status];
if (accumulator[index]) {
accumulator[index].push(currentValue);
} else {
accumulator[index] = [currentValue];
}
return accumulator;
}, []);
console.log(statusOrderInfo);
Explanation for what's going on in your code:
As you have used [].fill([]) to fill the Array. It would create only one empty Array object and use it to initialize the actual array. That's why the following snippet behaves the way it should behave :)
const statusOrderInfo = Array(3).fill([]);
statusOrderInfo[0].push(10);
console.log(statusOrderInfo);
/* Results:
[
[ 10 ],
[ 10 ],
[ 10 ]
]
*/
this is my solution:
let statuses = ["Reachable", "Busy", "Unknown"];
const tableData = [
{"Location": "London", "Status": "Unknown"},
{"Location": "Delhi", "Status": "Reachable" },
{"Location": "Berlin", "Status": "Unknown"},
{"Location": "Tokyo", "Status": "Busy"}
];
let result = statuses.map(status => tableData.filter(tableDataItem => tableDataItem.Status == status));
console.log(result);

How to Convert an Array of Objects into an Object of Associative Arrays in Javascript

I am receiving the following structure from a system. I am attempting to bend it into the form needed for a particular graph utilizing chartjs. Given the JSON data structure … an array of objects in an object:
{
"chart": [
{
"date": "2018-10-29",
"done": 3,
"todo": 10
},
{
"date": "2018-10-30",
"done": 4,
"todo": 7
},
{
"date": "2018-10-31",
"done": 5,
"todo": 12
}
]
}
I need the desired JSON data structure ... an object of arrays (in one array, in one object)
{
"chart": [{
"date": [
"2018-10-29",
"2018-10-29",
"2018-10-31"
],
"done": [
3,
4,
5
],
"todo": [
10,
7,
12
]
}]
}
I have attempted to use the .map function but I don't seem to have the correct map-fu.
You could take an object and get all keys with ther values in single array.
var data = { chart: [{ date: "2018-10-29", done: 3, todo: 10 }, { date: "2018-10-30", done: 4, todo: 7 }, { date: "2018-10-31", done: 5, todo: 12 }] },
result = { chart: data.chart.reduce((r, o) => {
Object.entries(o).forEach(([k, v]) => (r[k] = r[k] || []).push(v));
return r;
}, {})
};
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
What about using reduce ?
const output = input.reduce((acc, curr) => ({
date: acc.date.concat(curr.date),
done: acc.done.concat(curr.done),
todo: acc.todo.concat(curr.todo),
}), { date: [], done: [], todo: [] });
const chartData = {
chart: [output],
};
Reference for reduce is here : https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Array/reduce
Here's a very explicit solution. There may be some slicker Javascript solutions; certainly you can do multiple .map calls, but that makes it less efficient.
// Variables
var dates = [];
var doneValues = [];
var todoValues = [];
// Loop through the original data once, collect the data.
originalJSON.forEach(function(data) {
dates.push(data["date"]);
doneValues .push(data["done"]);
todoValues .push(data["todo"]);
});
// Put it all together.
return {"chart": [{"date": dates, "done": doneValues , "todo": todoValues}]};
Modify it to suit your needs.

Array Map using JS - Compare values to another array and return value from second array

I'd like to map this table's chapter_id and brother_id with the brothers and chapters table below and return the brothername and name field's respectively. Using js or jquery. I am using vuejs returning minutes array as a computed property. See below.
In sql it's be something like
select brothername from brothers where minute.brother_id = brothers.id ... and then set the brothername as the new value for brother_id
same thing goes for chapter_id:
select brothername from brothers where minute.brother_id = brothers.id ... and then set the brothername as the new value for brother_id
the resulting array or object should be:
Expected Array
[
{
"location":"UCLA",
"chapter_id":"Beta",
"brother_id":"Golpher",
"created_at":"2008-05-15 22:23:00",
"status":"Approved"
},
{ ... },
{
"location":"John's Deli",
"chapter_id":"Beta", notice the change in the array based on the ids
"brother_id":"Sheera", notice the change in the array based on the ids
"created_at":"2008-05-15 22:23:00",
"status":"Approved"
}
]
Minutes Table (original array)
[
{
"location":"UCLA",
"chapter_id":2,
"brother_id":1,
"created_at":"2008-05-15 22:23:00",
"status":"Approved"
},
{ ... },
{
"location":"John's Deli",
"chapter_id":2,
"brother_id":4,
"created_at":"2008-05-15 22:23:00",
"status":"Approved"
}
]
Chapter's Table
[
{
"id":1,
"letter_representation":"A",
"name":"Alpha",
"founded_at":"UCLA",
...
},
{ ... }
]
Brother's Table
[
{
"id":1,
"profile_id":1,
"chapter_id":1,
"brothername":"Golpher",
"firstname":"Jack",
...
},
{ ... },
{
"id":4,
"profile_id":4,
"chapter_id":1,
"brothername":"Sheera",
"firstname":"Jake",
...
}
]
Vue.js
computed: {
brothers () {
return this.$store.state.brothers
},
chapters () {
return this.$store.state.chapters
},
minutes () {
return this.$store.getters.model
}
},
I assume that you don't want to mutate objects in the original arrays with this operation.
Note You may want to handle the case where brother_id or chapter_id doesn't exist in the corresponding table. In the below example, it just sets the property value to undefined
const minutesTable = [{
"location": "UCLA",
"chapter_id": 2,
"brother_id": 1,
"created_at": "2008-05-15 22:23:00",
"status": "Approved"
}, {
"location": "John's Deli",
"chapter_id": 2,
"brother_id": 4,
"created_at": "2008-05-15 22:23:00",
"status": "Approved"
}]
const chapterTable = [{
"id": 1,
"letter_representation": "A",
"name": "Alpha",
"founded_at": "UCLA",
}]
const brotherTable = [{
"id": 1,
"profile_id": 1,
"chapter_id": 1,
"brothername": "Golpher",
"firstname": "Jack",
}, {
"id": 4,
"profile_id": 4,
"chapter_id": 1,
"brothername": "Sheera",
"firstname": "Jake",
}]
// your result
const result = minutesTable.map(m => {
const brother = brotherTable.find(b => b.id === m.brother_id)
const chapter = chapterTable.find(c => c.id === m.chapter_id)
return Object.assign({}, m, {
brother_id: brother && brother.brothername,
chapter_id: chapter && chapter.name,
})
})
console.log(result)
This should be what you need
const minutesTable = [
{
"location":"UCLA",
"chapter_id":2,
"brother_id":1,
"created_at":"2008-05-15 22:23:00",
"status":"Approved"
},
{
"location":"John's Deli",
"chapter_id":2,
"brother_id":4,
"created_at":"2008-05-15 22:23:00",
"status":"Approved"
}
]
const chapterTable =
[
{
"id":1,
"letter_representation":"A",
"name":"Alpha",
"founded_at":"UCLA",
}
]
const brotherTable = [
{
"id":1,
"profile_id":1,
"chapter_id":1,
"brothername":"Golpher",
"firstname":"Jack",
},
{
"id":4,
"profile_id":4,
"chapter_id":1,
"brothername":"Sheera",
"firstname":"Jake",
}
]
/* code starts here */
let newMinutesTable = JSON.parse(JSON.stringify(minutesTable)).map(a => {
let brother = brotherTable.find(id => id.id === a.brother_id);
let chapter = chapterTable.find(id => id.id === a.chapter_id)
if (brother) a.brother_id = brother.brothername
if (chapter) a.chapter_id = chapter.name;
return a;
})
console.log([minutesTable,newMinutesTable]);
I think you should prepare those values first just to better understanding. So I made this, let me explain in pieces.
Your input information:
var minutesTable = [{
"location": "UCLA",
"chapter_id": 2,
"brother_id": 1,
"created_at": "2008-05-15 22:23:00",
"status": "Approved"
}, {
"location": "John's Deli",
"chapter_id": 2,
"brother_id": 4,
"created_at": "2008-05-15 22:23:00",
"status": "Approved"
}],
chapterTable = [{
"id": 1,
"letter_representation": "A",
"name": "Alpha",
"founded_at": "UCLA",
}],
brotherTable = [{
"id": 1,
"profile_id": 1,
"chapter_id": 1,
"brothername": "Golpher",
"firstname": "Jack",
}, {
"id": 4,
"profile_id": 4,
"chapter_id": 1,
"brothername": "Sheera",
"firstname": "Jake",
}];
Somehow you'll be forced to take this information as variables. We will work with that.
Preparing data
Dealing with array of objects it's a litle bit complicated when you need to look for unique informations on each object from distinct arrays especially if you want to run this more than once. So instead of working with arrays of objects we can save our lifes changing that to objects of objects, where each item index must be that unique IDs. Look:
var chapters = {},
brothers = {};
chapterTable.map(function(el, i) {
chapters[el.id] = el;
});
brotherTable.map(function(el, i) {
brothers[el.id] = el;
});
Now you can easily find a chapter by chapter_id or a brother by brother_id, right? Then you can finish the problem like this:
var output = [];
minutesTable.map(function(el, i) {
var item = {
"location": el.location, // note that values are just default values, just in case
"chapter_id":"-",
"brother_id":"-",
"created_at": el.created_at,
"status": el.status
};
// PS: you need to check if that brother_id really exists!
if(brothers[el.brother_id] != undefined) {
item.brother_id = brothers[el.brother_id].brothername;
}
// PS: the same with chapters
if(chapters[el.chapter_id] != undefined) {
item.chapter_id = chapters[el.chapter_id].name;
}
output.push(item);
});
That's it. Anyway, if you can change your SQL queries, would be better to work with SQL joins and prepare your values there.

How to iterate JSON with multiple levels?

Welcome, I got a problem with JSON file. I would like to iterate through it and get every status into array, but I got stuck. I load this file and parse it. Then I tried to use forEach but it did not worked. Thanks for help!
[{
"offers": [{
"advertiser_api_id": 12,
"status": 1
}, {
"advertiser_api_id": 13,
"status": 0
}]
}]
I assume this will be in javascript. You can try the following:
for (x in json[0].offers) {
console.log(json[0].offers[x].status);
}
You have got an array Of Objects inside Array.
Firstly, you need to parse the data from JSON to object using JSON.parse(data); Then, access the object offers. using the parsedData[0].offers object and then iterate over the array to get the status.
var data = `[{
"offers": [{
"advertiser_api_id": 12,
"status": 1
}, {
"advertiser_api_id": 13,
"status": 0
}]
}]`;
var parsedData = JSON.parse(data);
var result = [];
parsedData[0].offers.forEach((currentValue) => result.push(currentValue["status"]));
console.log(result)
You can use map function:
var data = [{
"offers": [{
"advertiser_api_id": 12,
"status": 1
}, {
"advertiser_api_id": 13,
"status": 0
}]
}]
var stats = data[0].offers.map(function(item) {
return item.status;
})
console.log(stats);
This loops through the object and prints the data to console - you likely did not reach the correct array to loop through.
var data = [{
"offers": [{
"advertiser_api_id": 12,
"status": 1
}, {
"advertiser_api_id": 13,
"status": 0
}]
}];
data[0].offers.forEach(function(element) {
console.log(element.status);
});
This will work
statuses = []
JSON.parse(data)[0].offers.forEach(x => statuses.push(x.status))
A recursive approach
var item = [{
"offers": [{
"advertiser_api_id": 12,
"status": 1
}, {
"advertiser_api_id": 13,
"status": 0
}]
}];
var statusArray = [];
function getStatusForALlLevels(item){
if(item.offers instanceof Array){
for(var i=0;i<item.offers.length;i++){
getStatusForALlLevels(item.offers[i]);
}
}else{
statusArray.push(item.status);
}
}
getStatusForALlLevels(item[0]);
console.log(statusArray);
You could use an iterative and recursive approach for getting status from multiple nested objects.
var data = [{ offers: [{ advertiser_api_id: 12, status: 1 }, { advertiser_api_ii: 13, status: 0 }] }],
status = data.reduce(function iter(r, o) {
if ('status' in o) {
r.push(o.status);
}
Object.keys(o).forEach(function (k) {
if (Array.isArray(o[k])) {
r = o[k].reduce(iter, r);
}
});
return r;
}, []);
console.log(status);

Categories