Any help would be appreciated.
I have an array (A) that contains 3 arrays of objects.
A = [
[{date: '2022-12-05', value: 10.6},{date: '2022-12-06', value: 5.6},{date: '2022-12-07, value: 8.6}],
[{date: '2022-12-05', value: 4.2},{date: '2022-12-06', value: 12.3},{date: '2022-12-07, value: 9.5}],
[{date: '2022-12-05', value: 5.6},{date: '2022-12-06', value: 9.8},{date: '2022-12-07, value: 7.8}]
]
From this array (A), I need to get an array (B) that will contain the merged/concatenated arrays with new / renamed object keys (value1,value2,value3) as per the following format:
B = [
{date: '2022-12-05', value1: 10.6, value2: 4.2, value3: 5.6},
{date: '2022-12-06', value1: 5.6, value2: 12.3, value3: 9.8},
{date: '2022-12-07', value1: 8.6, value2: 9.5, value3: 7.8}
]
The length of the first array (A) may vary (1 / 2 / 3 or more), this is why I'm struggling to find a dynamic/efficient solution.
Many thanks
You could take a Map and collect all values.
const
data = [[{ date: '2022-12-05', value: 10.6 }, { date: '2022-12-06', value: 5.6 }, { date: '2022-12-07', value: 8.6 }], [{ date: '2022-12-05', value: 4.2 }, { date: '2022-12-06', value: 12.3 }, { date: '2022-12-07', value: 9.5 }], [{ date: '2022-12-05', value: 5.6 }, { date: '2022-12-06', value: 9.8 }, { date: '2022-12-07', value: 7.8 }]],
result = Array.from(
data
.flat()
.reduce((m, { date, value }) => m.set(date, [...m.get(date) || [], value]), new Map),
([date, values]) => values.reduce((o, v, i) => ({ ...o, [`value${i + 1}`]: v }), { date })
)
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You could use map() and reduce() functions:
const A = [
[{date: '2022-12-05', value: 10.6},{date: '2022-12-06', value: 5.6},{date: '2022-12-07', value: 8.6}],
[{date: '2022-12-05', value: 4.2},{date: '2022-12-06', value: 12.3},{date: '2022-12-07', value: 9.5}],
[{date: '2022-12-05', value: 5.6},{date: '2022-12-06', value: 9.8},{date: '2022-12-07', value: 7.8}]
];
const B = A.reduce((acc, curr) => {
curr.forEach(item => {
const existingItem = acc.find(i => i.date === item.date);
if (existingItem) {
existingItem[`value${A.indexOf(curr) + 1}`] = item.value;
} else {
acc.push({
date: item.date,
[`value${A.indexOf(curr) + 1}`]: item.value
});
}
});
return acc;
}, []);
console.log(B);
May i suggest a simpler data structure? Instead of numbering through the values in the form of 'value1, 'value2', ... 'valueN', you could use an array of values per date, which would make the transformation also a little bit easier as well:
const A = [
[{date: '2022-12-05', value: 10.6},{date: '2022-12-06', value: 5.6},{date: '2022-12-07', value: 8.6}],
[{date: '2022-12-05', value: 4.2},{date: '2022-12-06', value: 12.3},{date: '2022-12-07', value: 9.5}],
[{date: '2022-12-05', value: 5.6},{date: '2022-12-06', value: 9.8},{date: '2022-12-07', value: 7.8}]
]
//Flatten Array
const Af = A.flat();
//Group by date
const B = Af.reduce((acc, cur) => {
acc[cur['date']] = acc[cur['date']] ? [...acc[cur['date']], cur.value] : [cur.value]
return acc;
}, {});
console.log(B);
Related
I have 2 arrays with current week dates and investments with value and date. I want to return an array with the values that have corresponding dates between the 2 arrays.
My non-working solution is:
const daysOfWeek = [
"20-06-2022",
"21-06-2022",
"22-06-2022",
"23-06-2022",
"24-06-2022",
"25-06-2022",
"26-06-2022",
]
const investmentsData = [{
value: 0.77,
date: "21-06-2022"
},
{
value: 1.50,
date: "22-06-2022"
},
{
value: 0.80,
date: "20-06-2022"
},
{
value: 1.00,
date: "21-06-2022"
},
{
value: 0.77,
date: "20-06-2022"
},
{
value: 0.79,
date: "22-06-2022"
},
{
value: 0.73,
date: "18-06-2022"
},
{
value: 1.29,
date: "19-06-2022"
}
]
const result = investmentsData.flatMap((dayValue) => {
const getDayValue = daysOfWeek.filter((day) => {
return dayValue.date === day;
});
return getDayValue;
});
const filteredResult = result.filter((val) => !!val);
console.log(filteredResult)
// ["21-06-2022", "22-06-2022", "20-06-2022", "21-06-2022", "20-06-2022", "22-06-2022"]
When what I need is:
[0.77, 1.50, 0.80, 1.00, 0.77, 0.79]
Probably the filter inside the map is not the best option as it´s going to return the value of the first array (which is a date).
I also have the problem that result returns also the undefined. I then run filteredResult to remove all the undefined in the result. I guess this is a job that can be done with one function all together.
Take it step by step:
Filter investmentsData on whether or not daysOfWeek contains the date
From the filtered values, return the value.
const daysOfWeek = ["20-06-2022", "21-06-2022", "22-06-2022", "23-06-2022", "24-06-2022", "25-06-2022", "26-06-2022"];
const investmentsData = [
{ value: 0.77, date: "21-06-2022" },
{ value: 1.50, date: "22-06-2022" },
{ value: 0.80, date: "20-06-2022" },
{ value: 1.00, date: "21-06-2022" },
{ value: 0.77, date: "20-06-2022" },
{ value: 0.79, date: "22-06-2022" },
{ value: 0.73, date: "18-06-2022" },
{ value: 1.29, date: "19-06-2022" }
]
const result = investmentsData
.filter(d => daysOfWeek.includes(d.date))
.map(d => d.value);
console.log(result);
I have an object with an array which contains another array. I need to add up the values from these child arrays where the name matches each other.
let arr = {
expenses: [
{
id: 11,
freqs: [
{ name: "day", value: 100 },
{ name: "week", value: 200 },
{ name: "month", value: 300 },
],
},
{
id: 12,
freqs: [
{ name: "day", value: 100 },
{ name: "week", value: 200 },
{ name: "month", value: 300 },
],
},
{
id: 13,
freqs: [
{ name: "day", value: 100 },
{ name: "week", value: 200 },
{ name: "month", value: 300 },
],
},
],
};
In this example, I would need the results:
let result = [
{ name: "day", value: 300 },
{ name: "week", value: 600 },
{ name: "month", value: 900 },
];
I've been trying for ages with a combination of filter() and reduce() methods (unsure if these are the right way), but I just can't get it - it's really a headscratcher for me!
Thank you
This combines all the freqs into one array then sums their values into an object and then reformats that object to be an array of objects with the name and value keys.
const arr = {"expenses":[{"id":11,"freqs":[{"name":"day","value":100},{"name":"week","value":200},{"name":"month","value":300}]},{"id":12,"freqs":[{"name":"day","value":100},{"name":"week","value":200},{"name":"month","value":300}]},{"id":13,"freqs":[{"name":"day","value":100},{"name":"week","value":200},{"name":"month","value":300}]}]};
const res = Object.entries(
arr.expenses
.flatMap(({ freqs }) => freqs)
.reduce(
(acc, { name, value }) => Object.assign(acc, { [name]: (acc[name] ?? 0) + value }),
{}
)
).map(([name, value]) => ({ name, value }));
console.log(res);
If I didn't know the structure then this could be difficult. However, given your input, I think this should solve your problem.
// Your input
let arr = {
expenses: [
{
id: 11,
freqs: [
{ name: "day", value: 100 },
{ name: "week", value: 200 },
{ name: "month", value: 300 },
],
},
{
id: 12,
freqs: [
{ name: "day", value: 100 },
{ name: "week", value: 200 },
{ name: "month", value: 300 },
],
},
{
id: 13,
freqs: [
{ name: "day", value: 100 },
{ name: "week", value: 200 },
{ name: "month", value: 300 },
],
},
],
};
// My code
let result = new Array();
// Create each object structure and push into empty result array
arr.expenses[0].freqs.forEach((item)=>result.push({name: item.name, value: 0}));
// Map through each object in arr.expenses
arr.expenses.map((object)=>{
// Loop through each object in freqs
object.freqs.forEach((item)=>{
result.filter(eachItem=>{
// Check if result objs' name matches the freqs objs' name
if(eachItem.name==item.name){
eachItem.value+=item.value; // Add the values
}
})
});
});
// Check the output
console.log(result);
We want to reduce the frequences to a single element, so we can do:
let arr = { expenses: [ { id: 11, freqs: [ {name: "day", value: 100}, {name: "week", value: 200}, {name: "month", value: 300}, ], }, { id: 12, freqs: [ {name: "day", value: 100}, {name: "week", value: 200}, {name: "month", value: 300}, ], }, { id: 13, freqs: [ {name: "day", value: 100}, {name: "week", value: 200}, {name: "month", value: 300}, ], }, ], };
let result = arr.expenses.reduce((total, curr) => {
total[0].value += curr.freqs[0].value
total[1].value += curr.freqs[1].value
total[2].value += curr.freqs[2].value
return total
}, [{name: "day", value: 0}, {name: "week", value: 0}, {name: "month", value: 0}])
console.log(result)
I want to find the common elements of multiple array of objects based on a common property. In addition, if an element appears more than once, I want the resulting array to reflect the number of times it occurs in all the arrays.
I tried the following:
var arr = [
[
{ name: 'kiwi', value: 12 },
{ name: 'apple', value: 5 },
{ name: 'apple', value: 12 },
{ name: 'pizza', value: 33 },
{ name: 'pizza', value: 24 },
{ name: 'fish', value: 5 },
{ name: 'milk', value: 5 },
{ name: 'banana', value: 7 },
{ name: 'orange', value: 11 },
],
[
{ name: 'taco', value: 23 },
{ name: 'pizza', value: 78 },
{ name: 'apple', value: 12 },
{ name: 'pizza', value: 33 },
{ name: 'pizza', value: 24 },
{ name: 'fish', value: 5 },
{ name: 'pie', value: 1 },
{ name: 'cake', value: 3 },
{ name: 'banana', value: 7 },
{ name: 'beef', value: 123 },
{ name: 'lime', value: 72 },
{ name: 'pizza', value: 34 },
],
[
{ name: 'apple', value: 12 },
{ name: 'pizza', value: 33 },
{ name: 'pizza', value: 24 },
{ name: 'pizza', value: 23 },
{ name: 'fish', value: 5 },
{ name: 'banana', value: 7 },
{ name: 'banana', value: 77 },
]
];
function findArraysWithCommonName(arr) {
let arrays = [...arr];
var result = arrays.shift().reduce(function(res, v) {
if (arrays.every(function(a) {
return (a.filter(function(e) {
return e.name === v.name
}).length > 0);
})) res.push(v);
return res;
}, []);
return result;
}
console.log(findArraysWithCommonName(arr))
The result I got is:
[
{name: "apple", value: 5},
{name: "apple", value: 12},
{name: "pizza", value: 33},
{name: "pizza", value: 24},
{name: "fish", value: 5},
{name: "banana", value: 7}
]
I expect the output to be:
[
{name: "apple", value: 12},
{name: "pizza", value: 33},
{name: "pizza", value: 24},
{name: "fish", value: 5},
{name: "banana", value: 7}
]
or
[
{name: "apple", value: 5},
{name: "pizza", value: 33},
{name: "pizza", value: 24},
{name: "fish", value: 5},
{name: "banana", value: 7}
]
One approach would be to build a map that relates an object to it's "count" in the array (ie the number of times that object occours in arr).
This can be done via .reduce() where you serialize each object to a string via JSON.stringify(obj) - this string is a unique encoding of the corresponding object shape and state which is used as the key to identify the objects of this form in the mapping. The key is used to query and update the "count" value of the mapping, for each object encountered in the arr.
Once the mapping has been build, filter mapping entries by those with a "count" value greater than one.
Finally for any filtered entries, deserialize the corresponding keys of those entries via .map() to obtain an array of objects that occoured more that one in the original arr.
This approach could be implemented as:
var arr=[[{name:'kiwi',value:12},{name:'apple',value:5},{name:'apple',value:12},{name:'pizza',value:33},{name:'pizza',value:24},{name:'fish',value:5},{name:'milk',value:5},{name:'banana',value:7},{name:'orange',value:11}],[{name:'taco',value:23},{name:'pizza',value:78},{name:'apple',value:12},{name:'pizza',value:33},{name:'pizza',value:24},{name:'fish',value:5},{name:'pie',value:1},{name:'cake',value:3},{name:'banana',value:7},{name:'beef',value:123},{name:'lime',value:72},{name:'pizza',value:34}],[{name:'apple',value:12},{name:'pizza',value:33},{name:'pizza',value:24},{name:'pizza',value:23},{name:'fish',value:5},{name:'banana',value:7},{name:'banana',value:77}]];
/* Flatten array heirachy */
const flatArr = arr.flat();
/* Obtain a count mapping for each object's occourance in flatArr */
const mapObjectToCount = flatArr.reduce((map, item) => {
const key = JSON.stringify(item);
const count = (map[key] ? map[key] : 0) + 1;
return { ...map, [ key ] : count };
}, {})
/* Get key/value pair of the prior mapping, filter the objects by
those that occour more that one time, and obtain the original object
by parsing the key */
const result = Object.entries(mapObjectToCount)
.filter(([json, count]) => count > 1)
.map(([json]) => JSON.parse(json));
console.log(result)
I'd first transform each subarray into an object indexed by the number of occurences of each name. Then, iterate through each of those sub-objects created, creating a new object whose values are the minimum of the values found on the combined object, for every key.
Lastly, return a .filter of the first array, checking whether the occurence count of the name being iterated over on that object is greater than 0, reducing that count by one when found:
function findArraysWithCommonName(arr) {
const [oneArr, ...rest] = arr;
/* Transform each subarray into, eg:
{
"taco": 1,
"pizza": 4,
"apple": 1,
"fish": 1,
"pie": 1,
...
*/
const countsByName = rest.map(
subarr => subarr.reduce((a, { name }) => {
a[name] = (a[name] || 0) + 1;
return a;
}, {})
);
/* Combine the objects into one that contains only the minimum value for each property, eg:
{
"apple": 1,
"pizza": 3,
"fish": 1,
"banana": 1
}
*/
const combinedCountsByName = countsByName.reduce((a, countObj) => {
Object.entries(countObj).forEach(([key, val]) => {
countObj[key] = Math.min(a[key], val) || 0;
});
return countObj;
});
console.log(combinedCountsByName);
return oneArr.filter(({ name }) => {
if (combinedCountsByName[name] > 0) {
combinedCountsByName[name]--;
return true;
}
});
}
var arr = [
[
{ name: 'kiwi', value: 12 },
{ name: 'apple', value: 5 },
{ name: 'apple', value: 12 },
{ name: 'pizza', value: 33 },
{ name: 'pizza', value: 24 },
{ name: 'fish', value: 5 },
{ name: 'milk', value: 5 },
{ name: 'banana', value: 7 },
{ name: 'orange', value: 11 },
],
[
{ name: 'taco', value: 23 },
{ name: 'pizza', value: 78 },
{ name: 'apple', value: 12 },
{ name: 'pizza', value: 33 },
{ name: 'pizza', value: 24 },
{ name: 'fish', value: 5 },
{ name: 'pie', value: 1 },
{ name: 'cake', value: 3 },
{ name: 'banana', value: 7 },
{ name: 'beef', value: 123 },
{ name: 'lime', value: 72 },
{ name: 'pizza', value: 34 },
],
[
{ name: 'apple', value: 12 },
{ name: 'pizza', value: 33 },
{ name: 'pizza', value: 24 },
{ name: 'pizza', value: 23 },
{ name: 'fish', value: 5 },
{ name: 'banana', value: 7 },
{ name: 'banana', value: 77 },
]
];
console.log(findArraysWithCommonName(arr));
I am looking for some help in relation to the next code:
var items = [
{ name: 'Edward', value: 21 },
{ name: 'Sharpe', value: 37 },
{ name: 'And', value: 45 },
{ name: 'The', value: -12 },
{ name: 'Magnetic', value: 37 },
{ name: 'Zeros', value: 37 }
];
// Sort by value
items.sort(function (a, b) {
return a.value - b.value;
});
So, after I sort the items of the array in ascending or descending order, I want to extract the sub-array of objects that share the same value. Is there an easy approach for this in javascript?
The output related to previous example should be like:
[
{ name: 'Magnetic', value: 37 },
{ name: 'Sharpe', value: 37 },
{ name: 'Zeros', value: 37 }
]
Having the array of items sorted by value is good because you can take advantage of that and use Array.filter() to check if the previous item or the next one share the same value with the current inspected item on each iteration:
var items = [
{name: 'Edward', value: 21},
{name: 'Sharpe', value: 37},
{name: 'And', value: 45},
{name: 'The', value: -12},
{name: 'Magnetic', value: 37},
{name: 'Zeros', value: 37},
{name: 'foo', value: 21}
];
// Sort by value.
items.sort((a, b) => a.value - b.value);
console.log("Sorted items:", items);
// Filter elements that share same value.
let onlyDups = items.filter((o, i, arr) =>
{
return (i - 1 >= 0 && (arr[i - 1].value === o.value)) ||
(i + 1 < arr.length && arr[i + 1].value === o.value);
});
console.log("Items that share value:", onlyDups);
.as-console {background-color:black !important; color:lime;}
.as-console-wrapper {max-height:100% !important; top:0;}
You can use javascript Filter
items.filter((i)=>{
let duplicate = items.filter((x)=> {
return x.value === i.value;
})
// Return if has duplicate
return duplicate.length > 1
})
output:
[{ name: 'Magnetic', value: 37 }, { name: 'Sharpe', value: 37 }, { name: 'Zeros', value: 37 }]
I am trying to join an array of dates and values to an array of dates, without filtering out the extra dates. The LinqJS reference here is pretty confusing on how I would actually go about using the join. The question here did not help much either.
Edit:
Join documentation here: https://svschmidt.github.io/linqjs/Collection.html#Join
It looks like I need help sorting out how to get the join to be an outer and not an inner join so it includes null/undefined values.
Say I have two arrays:
Array 1:
[
'2017-02-10',
'2017-02-11',
'2017-02-12',
'2017-02-13',
'2017-02-20',
'2017-02-21',
'2017-02-22',
'2017-02-23',
'2017-02-24',
'2017-02-25',
'2017-02-26',
'2017-02-27'
]
Array 2:
[
{ date: '2017-02-10', value: 5 },
{ date: '2017-02-12', value: 8 },
{ date: '2017-02-13', value: 13 },
{ date: '2017-02-21', value: 14 },
{ date: '2017-02-24', value: 11 },
{ date: '2017-02-27', value: 7 }
]
I want to join them so I get this as my result (- for undefined):
[
'5',
-,
'8',
'13',
-,
'14',
-,
-,
'11',
-,
-,
'7'
]
My current syntax:
Enumerable.from(array1).join(array2, '$', '$.date', "outer, inner => inner.value").toArray()
This results in an array of the values, but the results are still inner joined, and it filters out what might be the null/undefined items.
How can I do this? How does the join syntax work for LinqJS?
I'm not sure about LinqJS, but regular JS is more than capable of doing this.
There are a couple ways to go about it. The reduce(), map(), and sort() functions are very Linq-esque and work well natively. (There is also filter() and a number of others).
const dates = [
'2017-02-10',
'2017-02-11',
'2017-02-12',
'2017-02-13',
'2017-02-20',
'2017-02-21',
'2017-02-22',
'2017-02-23',
'2017-02-24',
'2017-02-25',
'2017-02-26',
'2017-02-27'
]
const data = [
{ date: '2017-02-10', value: 5 },
{ date: '2017-02-12', value: 8 },
{ date: '2017-02-13', value: 13 },
{ date: '2017-02-21', value: 14 },
{ date: '2017-02-24', value: 11 },
{ date: '2017-02-27', value: 7 }
];
const result = dates
.map(date => ({ date, value: '-' }))
.concat(data)
.filter(({ date, value }) => !(data.find(d => d.date === date) && value === '-'))
.sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime());
console.log(result);
const justValues = Object.keys(result).map(key => result[key].value);
console.log(justValues);
You could use GroupJoin
Correlates the elements of two sequences based on equality of keys and groups the results. The default equality comparer is used to compare keys.
for a LEFT OUTER JOIN and specify the wanted value and use DefaultIfEmpty
Returns the elements of the specified sequence or the specified value in a singleton collection if the sequence is empty.
with the wanted replacement, if null.
var dates = ['2017-02-10', '2017-02-11', '2017-02-12', '2017-02-13', '2017-02-20', '2017-02-21', '2017-02-22', '2017-02-23', '2017-02-24', '2017-02-25', '2017-02-26', '2017-02-27'],
values = [{ date: '2017-02-10', value: 5 }, { date: '2017-02-12', value: 8 }, { date: '2017-02-13', value: 13 }, { date: '2017-02-21', value: 14 }, { date: '2017-02-24', value: 11 }, { date: '2017-02-27', value: 7 }],
result = Enumerable
.From(dates)
.GroupJoin(
values,
'',
'$.date',
't, u => ({ date: t, value: u.Select("$.value").DefaultIfEmpty("-").ToString() })')
.ToArray();
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/linq.js/2.2.0.2/linq.js"></script>
ES6
var dates = ['2017-02-10', '2017-02-11', '2017-02-12', '2017-02-13', '2017-02-20', '2017-02-21', '2017-02-22', '2017-02-23', '2017-02-24', '2017-02-25', '2017-02-26', '2017-02-27'],
values = [{ date: '2017-02-10', value: 5 }, { date: '2017-02-12', value: 8 }, { date: '2017-02-13', value: 13 }, { date: '2017-02-21', value: 14 }, { date: '2017-02-24', value: 11 }, { date: '2017-02-27', value: 7 }],
result = Enumerable
.From(dates)
.GroupJoin(
values,
'',
'$.date',
(t, u) => ({
date: t,
value: u.Select("$.value").DefaultIfEmpty("-").ToString()
}))
.ToArray();
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/linq.js/2.2.0.2/linq.js"></script>