I've been working on this for a couple of days, looked through all the other threads but can't seem to find a solution to my problem.
I start off with an object array, each specifying a 'start' and 'end' number. I loop through the difference between these, creating a new object from each number in between, storing the 'name' data in each new object created.
After which, I would like to sort the data based off the number, but maintaining the placement order of the original array.
const data = [{
'start': 10,
'finish': 14,
'name': 'one'
}, {
'start': 14,
'finish': 19,
'name': 'two'
}, {
'start': 12,
'finish': 16,
'name': 'three'
}]
let newData = [];
for (let d of data) {
const start = d.start;
const finish = d.finish;
for (let i = start; i <= finish; i++) {
newData.push({
'number': i,
'name': d.name
})
}
}
const sortData = (data) => newData.sort((a, b) => a.number - b.number)
const result = sortData(data)
// Test order for number 14
for(let r of result){
if(r['number'] == 14){
console.log(r.name)
}
}
// Show all data
console.log(result)
Sorry for the long snippet, but it's difficult for me to display the problem.
At the moment, when looking for the items with the number '14' after the sort, the names displayed are 'two, three, one'. I would like to instead maintain the original order of the 'data' array, so it should display as 'one, two, three'.
I'm not sure how sort() is deciding what goes where, because if I change the second object's finish number to 17 inside the original data array it will display the items for number '14' in the correct order.
Just to repeat - I would like to sort the newData array by the number but placed in the order of the original 'data' array.
I hope everything is clear! Please let me know if I can improve the question some how.
Here is a plunkr with the code - http://plnkr.co/edit/tM0h4C93CAnyy1g0T3jp?p=preview.
For a stable sort, you need another propery, like the index of the array.
With a new property,
const data = [{ start: 10, finish: 14, name: 'one' }, { start: 14, finish: 19, name: 'two' }, { start: 12, finish: 16, name: 'three' }]
let newData = [];
data.forEach((d, pos) => {
const start = d.start;
const finish = d.finish;
for (let i = start; i <= finish; i++) {
newData.push({ number: i, name: d.name, pos })
}
});
const sortData = (data) => newData.sort((a, b) => a.number - b.number || a.pos - b.pos)
const result = sortData(data)
// Test order for number 14
for(let r of result){
if(r['number'] == 14){
console.log(r.name)
}
}
console.log(result)
.as-console-wrapper { max-height: 100% !important; top: 0; }
or sort by name with an object.
const data = [{ start: 10, finish: 14, name: 'one' }, { start: 14, finish: 19, name: 'two' }, { start: 12, finish: 16, name: 'three' }]
let newData = [];
let order = Object.create(null);
data.forEach((d, pos) => {
const start = d.start;
const finish = d.finish;
order[d.name] = pos;
for (let i = start; i <= finish; i++) {
newData.push({ number: i, name: d.name })
}
});
const sortData = (data) => newData.sort((a, b) => a.number - b.number || order[a.name] - order[b.name])
const result = sortData(data)
// Test order for number 14
for(let r of result){
if(r['number'] == 14){
console.log(r.name)
}
}
console.log(result)
.as-console-wrapper { max-height: 100% !important; top: 0; }
Related
I am trying to make function to get top3 objects from an array based by props. My site can't load up so i think this function runs endlessly but i cant figure out why.
renderItems = (items) => {
let top3 = []
let tempArr = items
let allNumbers = []
while (top3.length < 3){
allNumbers = []
for(let i = 0; i < tempArr.length; i++){
allNumbers = [...allNumbers, tempArr[i].hearts]
}
const result = tempArr.filter(i => i.hearts === Math.max(...allNumbers))
top3 = [...top3, ...result]
let countOfdeleted = 0
for(let i = 0; i < result.length; i++){
tempArr.splice(result[i].id-countOfdeleted, 1)
countOfdeleted++
}
for(let i = 0; i < tempArr.length; i++){
tempArr[i].id = i
}
}
console.log(top3);
}
This answer is based on the assumption that 'items' is an array of objects and that each object in 'items' will have at-least 2 props namely 'id' and 'hearts'.
Further, there is no clarity on the significance of 'countOfdeleted' and 'tempArr'. Hence, it is assumed
that one needs to know how many elements of the 'items' array were not included (in the top3) as 'countOfdeleted'
that the remaining objects need to have their 'id' updated (based on index)
With the aforementioned assumptions, the below should implement the required logic:
const items = [
{ id: 0, hearts: 5 }, { id: 1, hearts: 4 }, { id: 2, hearts: 5 },
{ id: 3, hearts: 3 }, { id: 4, hearts: 5 }, { id: 5, hearts: 2 },
{ id: 6, hearts: 2 }, { id: 7, hearts: 1 }, { id: 8, hearts: 4 }
];
const getTop3 = (arr = items) => {
const maxVal = arr.reduce((fin, itm) => itm.hearts > fin ? itm.hearts : fin, 0);
const topAll = arr.filter(obj => obj.hearts === maxVal);
const sansTop3 = arr
.filter(obj => obj.hearts !== maxVal)
.map((obj, idx) => ({...obj, id: idx}));
console.log('countOfDeleted: ', arr.length - (topAll.length > 3 ? topAll.length : 3));
console.log('rest with updated-id (tempArr): ', sansTop3);
return topAll.length > 3 ? topAll.slice(0, 3) : [...topAll];
};
console.log('\ntop3:\n^^^^\n', getTop3());
Approach / Explanation
Find the 'maximum-value' (maxVal) based on the prop ('hearts')
Find all objects which have the props matching maxVal (stored in array 'topAll')
[Optional: Gather remaining 'items' elements and update their 'id' in 'sansTop3' array, to match the 'tempArr' in the question]
[Optional: Determine the number of 'items' deleted, to match countOfdeleted in the question]
If more than 3 elements have props ('heart') matching 'maxVal', return only 3; otherwise, return the all top-value element/s
I have a temp array which originally looks something like this :
[{emp_id:1, acitivty:'run'}, {emp_id: 2, activity:'climb'}, {emp_id:1,activity:'swim'} .....]
Now, what I want is to merge the objects with the same emp_id. It should look like this:
[{emp_id:1, activity:'run', activity2:'swim'}, {emp_id:'2',activity:'climb'} .....]
Instead, I only got this, not showing the rest of the objects in the array:
[{emp_id:'1', activity:'run', activity2:'swim'}]
In my code, when the condition is met and the merging and splicing is done, I decrement the z because I think after splicing, the array will be reindexed and I think the array length will still be as is.
What seems to be the problem?
for(var z = 0; z < temp.length; z++) {
for(var x = 1; x < temp.length; x++) {
if(temp[z].emp_id == temp[x].emp_id) {
var ot_key = 'ot_time'+x+1;
var status_key = 'status'+x+1;
var dtr_key = 'dtr_out'+x+1;
Object.assign(temp[z], {
[ot_key] : temp[x].ot_time,
[status_key] : temp[x].status,
[dtr_key] : temp[x].dtr_out
})
temp.splice(x, 1);
z--;
}
}
}
Beside the spelling, you could take an object and store the wanted postfix in the objects. later take the payload.
For example have a look to the final object before getting the values and mapping only the payload property:
{
1: {
index: 3,
payload: { emp_id: 1, activity: "run", activity2: "swim" }
},
2: {
index: 2,
payload: { emp_id: 2, activity: "climb" }
}
}
Here emp_id is taken as key for grouping and because of the wanted structure an index is grouped as well for having a value for futher activities.
var data = [{ emp_id: 1, activity: 'run' }, { emp_id: 2, activity: 'climb' }, { emp_id: 1, activity: 'swim' }],
result = Object
.values(data.reduce((r, { emp_id, activity }) => {
if (r[emp_id]) r[emp_id].payload['activity' + r[emp_id].index++] = activity;
else r[emp_id] = { index: 2, payload: { emp_id, activity } };
return r;
}, {}))
.map(({ payload }) => payload);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
A better approach is to use an array for the activities.
var data = [{ emp_id: 1, activity: 'run' }, { emp_id: 2, activity: 'climb' }, { emp_id: 1, activity: 'swim' }],
result = Object.values(data.reduce((r, { emp_id, activity }) => {
if (r[emp_id]) r[emp_id].activity.push(activity);
else r[emp_id] = { emp_id, activity: [activity] };
return r;
}, {}));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can use array .reduce() method to get an array of activities grouped by emp_id which I think much cleaner.
const temp = [{emp_id:1,activity:"run"},{emp_id:2,activity:"climb"},{emp_id:1,activity:"swim"}];
const res = Object.values(temp.reduce((ac, { emp_id, activity }) => {
ac[emp_id] = ac[emp_id] || {emp_id, activity: []}
ac[emp_id].activity.push(activity)
return ac;
}, {}));
console.log(res);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Everything you can do in 2 loop, one for reduce another to collect output array.values.
Fast and better.
Note : If you dont want to use Object.keys, use 2nd solution
const data = [
{ emp_id: 1, activity: "run" },
{ emp_id: 2, activity: "climb" },
{ emp_id: 1, activity: "swim" }
];
const result = data.reduce((obj, { emp_id, activity }) => {
if (!obj[emp_id]) obj[emp_id] = { emp_id };
const count = Object.keys(obj[emp_id]).length;
obj[emp_id] = {
...obj[emp_id],
[`activity_${count}`]: activity
};
return obj;
}, {});
let finalResult = Object.values(result)
console.log(finalResult);
// If you dont want to use Object.keys
const result2 = data.reduce((obj, { emp_id, activity }) => {
if (!obj[emp_id]) obj[emp_id] = { count: 0, data: {} };
const count = obj[emp_id].count + 1;
obj[emp_id] = {
data: {
...obj[emp_id].data,
[`activity_${count}`]: activity
},
count: count
};
return obj;
}, {});
let finalResult2 = [];
for (let key in result2) {
const { data } = result2[key];
finalResult2.push(data);
}
console.log(finalResult2);
.as-console-wrapper { max-height: 100% !important; top: 0; color: blue!important; }
maybe like this
var data = [{
emp_id: 1, activity: 'run'
}, {
emp_id: 2, activity: 'climb'
}, {
emp_id: 1, activity: 'swim'
}];
var lookup = {};
var counter = {};
for (var i = 0; i < data.length; i++) {
var item = data[i];
if (lookup[item.emp_id]) {
var found = lookup[item.emp_id];
lookup[item.emp_id]['activity' + counter[item.emp_id]] = item.activity;
counter[item.emp_id]++;
data.splice(i, 1);
i--;
} else {
lookup[item.emp_id] = item;
counter[item.emp_id] = 2;
}
}
console.log(data);
You could use array.reduce() like this:
Be careful, your initial array has a spelling mistake in activity property.
let x =[
{emp_id:1, activity:'run'},
{emp_id: 2, activity:'climb'},
{emp_id:1,activity:'swim'}
];
let finalResult =
x.reduce((result, currentValue, currentIndex, arr) =>
{
return [...result,
arr.filter(f => f.emp_id === currentValue.emp_id &&
!result.find(r=>r.emp_id === f.emp_id)
)
.reduce((subResult, currentItem, index) =>
{
return {emp_id:currentItem.emp_id, ...subResult, ...{[`activity${index ==0 ? '' : '_' + (index+1)}`]:currentItem.activity}};
}, undefined)
];
}, [])
.filter(item=> !!item);
console.log(finalResult);
I would heed #NinaScholz's advice to use an array for activity values which will be much easier to handle afterwards when compared to testing for different related properties (and to fix the spelling of acitivty), but take a naive approach in creating a result array by using forEach(), which I find more readable. For example:
const source = [{emp_id: 1, activity: 'run'}, {emp_id: 2, activity: 'climb'}, {emp_id: 1, activity: 'swim'}];
let results = [];
// For each source item, check whether a result object with the current emp_id exists.
source.forEach(o => results.filter(r => r.emp_id === o.emp_id).length == 1
// Add activity to existing result object.
? results[o.emp_id].activity.push(o.activity)
// Create a new result object
: results.push({ emp_id: o.emp_id, activity: [o.activity] })
);
console.log(results);
.as-console-wrapper { max-height: 100% !important; top: 0; }
this solution will also take care if you have more then two
activity have same id
var x=[{emp_id:1, acitivty:'run'}, {emp_id: 2, activity:'climb'}, {emp_id:1,activity:'swim'},{emp_id:1,activity:'swim3'} ]
let myMap = new Map()
x.forEach(element =>{
let alreadyExist=myMap.has(element.emp_id)
if(alreadyExist){
let tempelement=myMap.get(element.emp_id)
tempelement.count=tempelement.count?++tempelement.count:2;
tempelement['acitivty'+tempelement.count]=element.activity
myMap.set(element.emp_id, tempelement)
}else{
myMap.set(element.emp_id, element)
}
});
//convert map to Array
let newArray=Array.from(myMap.values())
console.log(JSON.stringify(newArray))
//[{"emp_id":1,"acitivty":"run","count":3,"acitivty2":"swim","acitivty3":"swim3"},{"emp_id":2,"activity":"climb"}]
how to count the value of object in new object values
lets say that i have json like this :
let data = [{
no: 3,
name: 'drink'
},
{
no: 90,
name: 'eat'
},
{
no: 20,
name: 'swim'
}
];
if i have the user pick no in arrays : [3,3,3,3,3,3,3,3,3,3,3,90,20,20,20,20]
so the output should be an array
[
{
num: 3,
total: 11
},
{
num: 90,
total: 1
},
{
num:20,
total: 4
}
];
I would like to know how to do this with a for/of loop
Here is the code I've attempted:
let obj = [];
for (i of arr){
for (j of data){
let innerObj={};
innerObj.num = i
obj.push(innerObj)
}
}
const data = [{"no":3,"name":"drink"},{"no":90,"name":"eat"},{"no":20,"name":"swim"}];
const arr = [3,3,3,3,3,3,3,3,3,3,3,20,20,20,20,80,80];
const lookup = {};
// Loop over the duplicate array and create an
// object that contains the totals
for (let el of arr) {
// If the key doesn't exist set it to zero,
// otherwise add 1 to it
lookup[el] = (lookup[el] || 0) + 1;
}
const out = [];
// Then loop over the data updating the objects
// with the totals found in the lookup object
for (let obj of data) {
lookup[obj.no] && out.push({
no: obj.no,
total: lookup[obj.no]
});
}
document.querySelector('#lookup').textContent = JSON.stringify(lookup, null, 2);
document.querySelector('#out').textContent = JSON.stringify(out, null, 2);
<h3>Lookup output</h3>
<pre id="lookup"></pre>
<h3>Main output</h3>
<pre id="out"></pre>
Perhaps something like this? You can map the existing data array and attach filtered array counts to each array object.
let data = [
{
no: 3,
name: 'drink'
},
{
no:90,
name: 'eat'
},
{
no:20,
name: 'swim'
}
]
const test = [3,3,3,3,3,3,3,3,3,3,3,90,20,20,20,20]
const result = data.map((item) => {
return {
num: item.no,
total: test.filter(i => i === item.no).length // filters number array and then checks length
}
})
You can check next approach using a single for/of loop. But first I have to create a Set with valid ids, so I can discard noise data from the test array:
const data = [
{no: 3, name: 'drink'},
{no: 90, name: 'eat'},
{no: 20, name: 'swim'}
];
const userArr = [3,3,3,3,3,3,3,3,7,7,9,9,3,3,3,90,20,20,20,20];
let ids = new Set(data.map(x => x.no));
let newArr = [];
for (i of userArr)
{
let found = newArr.findIndex(x => x.num === i)
if (found >= 0)
newArr[found].total += 1;
else
ids.has(i) && newArr.push({num: i, total: 1});
}
console.log(newArr);
I have an array that I successfully map, creating new arrays. Now I would like to create a master one containing all smaller arrays. I understand you can push it in. But is there a better (less code) way to do it?
Here is my code:
obj = {
data: [
{
temp: 1,
air: 2,
cloud: 3
},
{
temp: 100,
air: 101,
cloud: 102
}
]
};
/*
var temp = obj.data.map(a => a.temp); // middle step
var air = obj.data.map(a => a.air); // middle step
var OneArrayToRuleThemAll = [];
OneArrayToRuleThemAll.push(temp,air);
console.log(OneArrayToRuleThemAll);
*/
// It may be easier for you to understand, when I simply comment out half of the // code and get straight to expected result.
Expected result: a new array that will contain:
[
[
1,
100
],
[
2,
101
]
]
In other words: can I somehow map/ loop through main obj and create OneArrayToRuleThemAll without middle steps?
My expected outcome is supposed to be the same as OneArrayToRuleThemAll - achieved without middle steps.
Thanks
You could use an array for the keys and transpose the array to the wanted style.
var obj = { data: [{ temp: 1, air: 2, cloud: 3 }, { temp: 100, air: 101, cloud: 102 }] },
keys = ['temp', 'air'],
result = obj.data.reduce(
(r, o) => (keys.forEach((k, i) => (r[i] = (r[i] || [])).push(o[k])), r),
[]
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
var temp = []
var air = []
var OneArrayToRuleThemAll = []
var length = obj.data.length
var count = 0
obj.data.map( (o) => {
count ++
temp.push(o.temp)
air.push(o.air)
if (count == length){
OneArrayToRuleThemAll.push(temp, air)
}
})
I am getting [{id:'test', time: '1223'}, {id: 'test2', time: '2323'}] from the backend, I need to retrieve the time values for all, perform a function and send back into the object as [{id:'test', time: 'new value for test'}, {id: 'test2', time: 'new value for test2'}]
So I decided I would try and extract the time values and push into array using for loop but my for loop is missing something
var objext = [{id:'test', time: 'blah'}, {id: 'test2', time: 'blah'}];
var dataArray = new Array;
for (var id in objext) {
dataArray.push(objext[id]);
};
It returns the same obviously as I don't know how to select the time key.
And then I'll run my function for each as such which is working well:
var time = [23,34,56];
var map = time.map(function(num) {
if (num === 23) {
return num +2;
} else {
return num;
};
});
And then the final part will be to put back and I'm thinking I could get an array of id and one of time and then slot back in together but the code I have so far can only map one array at a time:
var data = ["Drake","Ola","d"],
result = data.map(function (a) { return { id: a }; });
I have tried using an array as such:
var data = ["Drake","Ola","d"];
var hht = [21,34,56];
result = [data,hht].map(function (a,b) { return { id: a, time:b }; });
It did not map it properly.
Any ideas?
You can perform all of the tasks withing .map() callback
var objtext = [{id:'test', time: '1223'}, {id: 'test2', time: '2323'}];
objtext = objtext.map(function(obj) {
var time = obj.time;
// do stuff with `obj.time`
if (time == 1223) {
time += 2;
obj.time = time;
}
if (time == 2323) {
time -= 2;
obj.time = time;
}
return obj
});
To create an array containing only time properties
var times = objtext.map(function({time}) {return time});
You could use an array, iterate it and use from the other parts as well for a new object.
var data = ["Drake", "Ola", "d"],
hht = [21, 34, 56],
result = data.map(function (a, i) {
return { id: a, time: hht[i] };
});
console.log(result);
You can map the array of objects and return a different time with Object.assign:
var objs = [
{id: 'Drake', time: 'blah'},
{id: 'Ola', time: 'blah'},
{id: 'd', time: 'eh' }];
var hht = [21, 34, 56];
objs = objs.map( (obj, i) => Object.assign({}, obj, { time: hht[i] }) );
console.log(objs);
Try this using for loop.
var res = [{
id: 'test',
time: '1223'
}, {
id: 'test2',
time: '2323'
}, {
id: 'test3',
time: '2453'
}],
hht = [21, 34, 56];
for (var i = 0; i < res.length; i++) {
res[i]["time"] = +res[i]["time"] + hht[i];
//or use if statements
//if(+res[i]["time"] === 23)res[i]["time"] = +res[i]["time"] + hht[i];
//scenario will be
//res[0]["time"] = 1223 + 21
//res[1]["time"] = 2323 + 34
//res[2]["time"] = 2453 + 56
}
console.log(res);