Javascript to manipulate object structure - javascript

I have a javascript object with the following schema:
[{
face:"n",
values:[
{time:"15:00",value:5}
{time:"15:02",value:6}
]
},{
face:"e",
values:[
{time:"15:01",value:7}
{time:"15:02",value:8}
]
},{
face:"s",
values:[
{time:"15:01",value:7}
{time:"15:02",value:8}
]
},{
face:"w",
values:[
{time:"15:01",value:7}
{time:"15:02",value:8}
]
}]
How to convert it to the following structure:
[
{time:"15:00","n":5,"e":null,"s":null,"w":null},
{time:"15:01","n":null,"e":7,"s":7,"w":7},
{time:"15:02","n":6,"e":8,"s":8,"w":8},
]
The number of faces will be fixed (north, east, south, west)
It is possible that some timestamps are missing. In that case we need to fill the value with 'null' (see example above).

You can easily achieve the result using Map, reduce and forEach
with this approach the ordering is not particular, If you want the exact time ordering then you can sort it using the following custorm sort comparator function
result.sort((a, b) => +a.time.match(/\d+/g).join("") - b.time.match(/\d+/g).join(""))
const arr = [
{
face: "n",
values: [
{ time: "15:00", value: 5 },
{ time: "15:02", value: 6 },
],
},
{
face: "e",
values: [
{ time: "15:01", value: 7 },
{ time: "15:02", value: 8 },
],
},
{
face: "s",
values: [
{ time: "15:01", value: 7 },
{ time: "15:02", value: 8 },
],
},
{
face: "w",
values: [
{ time: "15:01", value: 7 },
{ time: "15:02", value: 8 },
],
},
];
const map = new Map();
const result = [
...arr.reduce((acc, { face, values }) => {
values.forEach(({ time, value }) => {
if (!acc.has(time))
acc.set(time, { time, n: null, e: null, s: null, w: null });
acc.get(time)[face] = value;
});
return acc;
}, map)
.values(),
];
console.log(result);
/* This is not a part of answer. It is just to give the output full height. So IGNORE IT */
.as-console-wrapper { max-height: 100% !important; top: 0; }

Because you haven't added what you tried so far (which is kinda useless for this task, IMHO), I will just answer in form of how I would tackle this task.
Locate your unique value in your expected output: time.
Loop through your array, and create an object with keys based on time. Save the properties name and each of the four faces, by first creating null values for all faces, and then filling it out with one value at time as a new timestamp appears in the array.
Here is how the whole object will look like:
{
"15:00": {time:"15:00","n":5,"e":null,"s":null,"w":null},
"15:01": {time:"15:01","n":null,"e":7,"s":7,"w":7},
"15:02": {time:"15:02","n":6,"e":8,"s":8,"w":8
}
Loop through the object—with for... in, [edit] or by simply using Object.values—to create an array out of it.
You can probably use some smart Array.reduce() functionality to minimize the code.

let old_array = [{
face: "n",
values: [
{ time: "15:00", value: 5 },
{ time: "15:02", value: 6 }
]
}, {
face: "e",
values: [
{ time: "15:01", value: 7 },
{ time: "15:02", value: 8 }
]
}, {
face: "s",
values: [
{ time: "15:01", value: 7 },
{ time: "15:02", value: 8 }
]
}, {
face: "w",
values: [
{ time: "15:01", value: 7 },
{ time: "15:02", value: 8 }
]
}];
let new_array = []
let times = {}
let faces = {"n": "north", "e": "east", "s": "south", "w": "west"}
old_array.forEach((obj) => {
obj.values.forEach((val) => {
if(val.time in times)
times[val.time][faces[obj.face]] = val.value
else {
let newT = {time: val.time, "north": null, "east": null, "south": null, "west": null}
newT[faces[obj.face]] = val.value
times[val.time] = newT
new_array.push(newT)
}
})
})
new_array.sort((a,b)=>a.time < b.time ? -1 : (a.time > b.time ? 1 : 0))
console.log(new_array)
// Expected output
// [
// { time: '15:00', north: 5, east: null, south: null, west: null },
// { time: '15:01', north: null, east: 7, south: 7, west: 7 },
// { time: '15:02', north: 6, east: 8, south: 8, west: 8 }
// ]

Related

Convert objects in nested array of objects

I'm having a data structure that looks like this:
[
[
{ word: "china", count: 0 },
{ word: "kids", count: 1 },
{ word: "music", count: 0 },
],
[
{ word: "china", count: 3 },
{ word: "kids", count: 0 },
{ word: "music", count: 2 },
],
[
{ word: "china", count: 10 },
{ word: "kids", count: 3 },
{ word: "music", count: 2 },
]
];
I want to calculate the minimum and maximum number of counts for each value of the property word in the nested array.
For example, the maximum counts for the word "china" is 10 and the minimum is 0.
I would like to accomplish something like this:
{ word: "china", min: 0, max: 10 }
How do I do this?
There are lots of ways to solve this. If you first transform the data into an object whose keys are the words and whose values are the counts of each word, then you can perform any number of operations on the counts (not just finding min and max):
function transform (array) {
const counts = {};
for (const {count, word} of array.flat()) {
(counts[word] ??= []).push(count);
}
return counts;
}
const input = [
[
{ word: "china", count: 0 },
{ word: "kids", count: 1 },
{ word: "music", count: 0 },
],
[
{ word: "china", count: 3 },
{ word: "kids", count: 0 },
{ word: "music", count: 2 },
],
[
{ word: "china", count: 10 },
{ word: "kids", count: 3 },
{ word: "music", count: 2 },
],
];
const transformed = transform(input);
console.log(transformed);
//=> { china: [ 0, 3, 10 ], kids: [ 1, 0, 3 ], music: [ 0, 2, 2 ] }
for (const [word, counts] of Object.entries(transformed)) {
const sorted = [...counts].sort((a, b) => a - b);
console.log({
word,
min: sorted.at(0),
max: sorted.at(-1),
});
}
//=>
// { word: "china", min: 0, max: 10 }
// { word: "kids", min: 0, max: 3 }
// { word: "music", min: 0, max: 2 }
Ref:
Array.prototype.flat()
Logical nullish assignment operator (??=)
Object.entries()
Shallow cloning an array using spread syntax (...)
Using a custom compare function with Array.prototype.sort()
Array.prototype.at()
You can use Array#reduce to collect the minimum and maximum values for a given word while looping over the array.
let arr=[[{word:"china",count:0},{word:"kids",count:1},{word:"music",count:0},],[{word:"china",count:3},{word:"kids",count:0},{word:"music",count:2},],[{word:"china",count:10},{word:"kids",count:3},{word:"music",count:2},]];
const getStats = (arr, w) => arr.flat().reduce((acc, {word, count})=>{
if (w === word) acc.min = Math.min(acc.min, count),
acc.max = Math.max(acc.max, count);
return acc;
}, { word : w, min: Infinity, max: -Infinity});
console.log(getStats(arr, 'china'));

I want to combine the values ​of objects in different arrays by sorting them one side

I want to concatenate the values ​​of objects in different arrays to one side.
I tried to output the data value received in json to console.log.
I want to put the values ​​in the Ingredient List into the List array.
console.log(detail);
{
List: [
{
id: 120,
content: "stack-overflow",
functionalList: [
{
id: 832,
},
],
},
{
id: 230,
content: "heap-overflow",
functionalList: [
{
id: 24,
},
],
},
],
ListValue: [
{
IngredientList: [
{
id: 1,
value: 43
},
{
id: 23,
value: 23
},
],
},
],
},
]);
I want to put ListValue -> IngredientList value values ​​into List array object.
How can I do it this way? I've been trying all day, but it's hard for me.
{
List: [
{
id: 120,
content: "stack-overflow",
value: 43
functionalList: [
{
id: 832,
functionalId: 37
},
],
},
{
id: 230,
content: "heap-overflow",
value: 23
functionalList: [
{
id: 24,
functionalId: 12
},
],
},
],
ListValue: [
{
IngredientList: [
{
id: 1,
value: 43
},
{
id: 23,
value: 23
},
],
},
],
},
]);
This should work in a mutable approach, even if you have multiple objects inside ListValue:
data.List = [
...data.List,
...data.ListValue.reduce((arr, el) => {
arr.push(...el.IngredientList);
return arr;
}, []),
];
It's not clear which value of the IngredientList should go in which item of List. Supposing you always want to pair the first value with the first item, the second with the second, and so on...
const obj = {
List: [
{
id: 120,
content: "stack-overflow",
functionalList: [
{
id: 832,
},
],
},
{
id: 230,
content: "heap-overflow",
functionalList: [
{
id: 24,
},
],
},
],
ListValue: [
{
IngredientList: [
{
id: 1,
value: 43,
},
{
id: 23,
value: 23,
},
],
},
],
};
const ingridientsValue = obj.ListValue[0].IngredientList.map(el => el.value); // [43, 23]
for (const item of obj.List) item.value = ingridientsValue.shift();
console.log(obj.List);
I have solved this. Check it out here: https://jsfiddle.net/bowtiekreative/o5rhy7c1/1/
First your JSON needs to be validated.
Remove the ")" and the extra ","
Instructions
Create an empty array called arr.
Loop through the ListValue array.
For each item in the ListValue array, loop through the IngredientList array.
For each item in the IngredientList array, push the value property into the arr array.
Log the arr array to the console.
Example:
var json = {
"List":[
{
"id":120,
"content":"stack-overflow",
"functionalList":[
{
"id":832
}
]
},
{
"id":230,
"content":"heap-overflow",
"functionalList":[
{
"id":24
}
]
}
],
"ListValue":[
{
"IngredientList":[
{
"id":1,
"value":43
},
{
"id":23,
"value":23
}
]
}
]
};
var arr = [];
for (var i = 0; i < json.ListValue.length; i++) {
for (var j = 0; j < json.ListValue[i].IngredientList.length; j++) {
arr.push(json.ListValue[i].IngredientList[j].value);
}
}
console.log(arr)

Is there a good way to combine array and object in JavaScript?

There is an object and an array.
list: [
{
oldData: {
title: 'abc',
id: 1,
date: '1982-09-30',
budget: 250000,
},
newData: [
{
key: 1,
data: null,
value: 5,
},
{
key: 2,
data: null,
value: 22,
},
...
],
},
{
oldData: {
title: 'blablablaaaaa',
id: 2,
date: '2012-02-23',
budget: 350000,
},
newData: [
{
key: 1,
data: null,
value: 35,
},
{
key: 2,
data: null,
value: 41,
},
...
],
},
... some more datas...
]
as above, There is more data of the same type.
I need to use oldData and newData together, so I want to combine the two.
How can I combine oldData and newData so that there are multiple sets of oldData and newData pairs?
for example, [{ combineData: {...} }, { combineData: {...} }, ... }] here.
I know how to combine array and array, object and object, but I do not know how to do that.
Is there any good solution?
Your desired result is unclear, but you did say you wanted old/new pairs. This answer is different than the others in that it produces an array of combined data objects made of old/new pairs, where the oldData values are duplicated in order to appear alongside each corresponding newData value within its list item.
your original data after the first question update:
let list = [
{
oldData: { title: 'abc', id: 1, date: '1982-09-30', budget: 250000 },
newData: [
{ key: 1, data: null, value: 5 },
{ key: 2, data: null, value: 22 },
//...
],
},
{
oldData: { title: 'blablablaaaaa', id: 2, date: '2012-02-23', budget: 350000 },
newData: [
{ key: 1, data: null, value: 35 },
{ key: 2, data: null, value: 41 },
//...
],
},
//... some more datas...
];
This code maps each {old,new[]} list item into arrays of pairs [{old,new}, {old,new}, ...] that are combined in the final reduce() call:
var combinedDatas = list
.map(listItem => listItem.newData.map(newItem => ({
oldData: listItem.oldData,
newData: newItem
})))
.reduce();
console.log(JSON.stringify(oldNewCombos, null, 4));
produces a list of denormalized pairs:
[
{ list[0].oldData, list[0].newData[0] },
{ list[0].oldData, list[0].newData[1] },
//...rest of list[0] oldData with newData[n] combos
{ list[1].oldData, list[1].newData[0] },
{ list[1].oldData, list[1].newData[1] },
//...rest of list[1] oldData with newData[n] combos
{ list[2].oldData, list[2].newData[0] },
{ list[2].oldData, list[2].newData[1] },
//...rest of list[2] oldData with newData[n] combos
//...
]
You can use map() on the array. And use Object.assign() and spread operator to combine all the properties of all the elements in newData into one object.
const arr = [
{
oldData: {
a:10,
b:20
},
newData: [
{
c:30,
d:40
},
{
e:50,
f:60
}
],
}
]
const res = arr.map(x => ({combinedData:{...x.oldData, ...Object.assign({}, ...x.newData)}}))
console.log(res)
You can map over the array and use object destructuring (...object) to create a new, combined object:
const data = [
{
oldData: {
foo: 'lorem',
},
newData: {
bar: 'ipsum',
},
},
{
oldData: {
foo: 'dolor',
},
newData: {
bar: 'sit amet',
},
},
];
const combined = data.map(record => ({...record.oldData, ...record.newData}));
console.log(combined);
This will overwrite duplicate keys however, so something like:
{
oldData: {
message: 'a',
},
newData: {
message: 'b',
},
}
will become:
{
message: 'b',
}

Sort JavaScript Array in Artificial Order

I have an array returned by a REST query that represents the number of items in a multi-step production process.
var steps = [
{ name:'Package', value:3},
{ name:'Assemble', value:1 },
{ name:'Ship', value:7},
{ name:'Preprocess', value:9 },
{ name:'Paint', value:5 }
];
I'd like to sort them in the order of the process, like this:
Preprocess
Paint
Assemble
Package
Ship
I have other alphanumeric sorts that I am doing with Underscore but I cannot figure this one out.
You could take an object for the wanted order with numerical values for the position. Then sort by this values.
var steps = [{ name: 'Package', value: 3 }, { name: 'Assemble', value: 1 }, { name: 'Ship', value: 7 }, { name: 'Preprocess', value: 9 }, { name: 'Paint', value: 5 }],
order = { Preprocess: 1, Paint: 2, Assemble: 3, Package: 4, Ship: 5 };
steps.sort(({ name: a }, { name: b }) => order[a] - order[b]);
console.log(steps);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can create an object which mentions the priority on each name while ordering.
let priority = {
Preprocess: 1,
Paint: 2,
Assemble: 3,
Package: 4,
Ship: 5,
}
var steps = [
{ name:'Package', value:3},
{ name:'Assemble', value:1 },
{ name:'Ship', value:7},
{ name:'Preprocess', value:9 },
{ name:'Paint', value:5 }
];
const result = steps.sort((a,b) => priority[a.name] - priority[b.name]);
console.log(result);
I'm just using a precedence map for every step type so that I can use it in the compare function.
var steps = [
{ name: 'Package', value: 3 },
{ name: 'Assemble', value: 1 },
{ name: 'Ship', value: 7 },
{ name: 'Preprocess', value: 9 },
{ name: 'Paint', value: 5 }
];
var stepPrecedenceMap = new Map([["Preprocess", 1], ["Paint", 2], ["Assemble", 3], ["Package", 4], ["Ship", 5]])
console.log(
steps.sort((stepA, stepB) => {
return stepPrecedenceMap.get(stepA.name) - stepPrecedenceMap.get(stepB.name)
}));

What's the most efficent way to populate an a property of an array of objects with a larger array with data?

I have a small array of objects with properties, like so:
[
{
topicName: 'Clicks',
topic: 1,
dates: [ <PLACE VALUES HERE> ],
},
{
topicName: 'Cost',
topic: 2,
dates: [ <PLACE VALUES HERE> ],
},
];
Then I have a large array of objects that I wish to extract some of the properties from in to the above dates array.
Here's what the data I wish to extract from:
[
{
"date": "2014-02-01",
"device": "Computer",
"match-type": "NA",
"clicks": 66,
"revenue": 1037,
"conversions": 2,
"cost": 284.35,
"impressions": 5330,
"ROI": 3.64691401441885
},
{
"date": "2014-02-01",
"device": "Tablet",
"match-type": "NA",
"clicks": 38,
"revenue": 587,
"conversions": 2,
"cost": 194.01000000000005,
"impressions": 1934,
"ROI": 3.025617236224936
},
{
"date": "2014-02-02",
"device": "Tablet",
"match-type": "NA",
"clicks": 40,
"revenue": 587,
"conversions": 2,
"cost": 190,
"impressions": 1934,
"ROI": 3.025617236224936
},
]
Now I need the data from all of the members of the last array and insert that releveant data for the particular object in the first array (totalling where necessary), like so:
[
{
topicName: 'Clicks',
topic: 1,
dates: [
{
date: '2014-02-01',
value: 104
},
{
date: '2014-02-02',
value: 40
}
],
},
{
topicName: 'Cost',
topic: 2,
dates: [
{
date: '2014-02-01',
value: 284,3519401
},
{
date: '2014-02-02',
value: 190
}
],
},
];
The target is the latest version of Chrome and I'm using Webpack with Babel so all the latest stuff is available.
Assuming the last dataset can be pretty large, what's the most efficient way to go about this?
[EDIT]
This is what I've come up with so far:
const dataAdapter = rawData => {
const topics = ['clicks', 'revenue', 'cost', 'roi'];
const topicsData = topics.map((topic, index) => {
const thisTopic = {};
thisTopic.topicName = topic;
thisTopic.topic = index;
thisTopic.dates = [];
return thisTopic;
});
const convertedData = topicsData.map(topicData => {
const thisTopic = topicData;
const map = new Map();
rawData.forEach(elem => {
map.set(elem.date, (map.get(elem.date) || 0) + elem[[thisTopic.topicName]]);
});
thisTopic.dates = Array.from(map);
return thisTopic;
});
return convertedData;
};
Thanks,
/J
You could take an object as reference to the wanted keys and date. Then iterate data and check if a reference to a result set exists. If not, create a new result set.
var result = [{ topicName: 'Clicks', topic: 1, dates: [], }, { topicName: 'Cost', topic: 2, dates: [], }],
data = [{ date: "2014-02-01", device: "Computer", "match-type": "NA", clicks: 66, revenue: 1037, conversions: 2, cost: 284.35, impressions: 5330, ROI: 3.64691401441885 }, { date: "2014-02-01", device: "Tablet", "match-type": "NA", clicks: 38, revenue: 587, conversions: 2, cost: 194.01000000000005, impressions: 1934, ROI: 3.025617236224936 }, { date: "2014-02-02", device: "Tablet", "match-type": "NA", clicks: 40, revenue: 587, conversions: 2, cost: 190, impressions: 1934, ROI: 3.025617236224936 }],
hash = { clicks: { _: result[0].dates }, cost: { _: result[1].dates }, };
data.forEach(function (o) {
['clicks', 'cost'].forEach(function (k) {
if (!hash[k][o.date]) {
hash[k][o.date] = { date: o.date, value: o[k] };
hash[k]._.push(hash[k][o.date]);
return;
}
hash[k][o.date].value += o[k];
});
});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Assuming your data is in data variable and topics are in topics variable. This solution uses only javascript builtin objects.
const getPropDateMap = (obj, prop) => obj
.reduce((accObj, item) => {
return Object.assign(accObj, {
[item.date]: item.clicks + (accObj[item.date] || 0)
})
}, {})
topics.forEach(topic => {
topic.dates = Object
.entries(getPropDateMap(data, topic.topicName.toLowerCase()))
.map(entry => ({date: entry[0], value: entry[1]}))
})

Categories