Set zero for missing data in array of objects - javascript

I have the following arrays of objects, for example:
const data = [
{
date: '01-01',
products: [
{
id: 1,
value: 6,
label: 'Product 1'
},
{
id: 2,
value: 3,
label: 'Product 2'
}
]
},
{
date: '02-01',
products: [
{
id: 1,
value: 4,
label: 'Product 1'
},
]
},
{
date: '03-01',
products: [
{
id: 1,
value: 11,
label: 'Product 1'
},
{
id: 2,
value: 15,
label: 'Product 2'
}
]
}
]
Then I do the grouping and get the following result:
const output = [
{
id: 1,
name: 'Product 1',
data: [6, 4, 11]
},
{
id: 2,
name: 'Product 2',
data: [3, 15]
}
]
The problem with the solution is that I cannot take into account the missing value (the object with the date "02-01" does not have an object with id: 2). I need to check that the object does not exist and substitute zero instead of the missing value. Maybe you know how to do it?
Solution code below:
const result = data.map(e => e.products).flat().reduce((acc, product) => {
const index = acc.findIndex(item => item.id === product.id);
if(index === -1) {
acc.push({
id: product.id,
name: product.label,
data: [product.value]
})
return acc;
}
const findIndex = acc[index].data.findIndex((innerNode) => innerNode.id === product.id);
if (findIndex === -1) {
console.log(product.value)
acc[index].data.push(product.value);
return acc;
}
return acc;
}, []);
Expected result:
const output = [
{
id: 1,
name: 'Product 1',
data: [6, 4, 11]
},
{
id: 2,
name: 'Product 2',
data: [3, 0, 15]
}
]

You can do this in three passes:
first, you find all dates. When you first encounter a product, you will set all its values to 0 for each of those dates.
then, you iterate products and ensure that, for each date, they have a value - which will be zero by default.
finally, you format the output.
const data = [
{
date: '01-01',
products: [
{
id: 1,
value: 6,
label: 'Product 1'
},
{
id: 2,
value: 3,
label: 'Product 2'
}
]
},
{
date: '02-01',
products: [
{
id: 1,
value: 4,
label: 'Product 1'
},
]
},
{
date: '03-01',
products: [
{
id: 1,
value: 11,
label: 'Product 1'
},
{
id: 2,
value: 15,
label: 'Product 2'
}
]
}
]
// goal is to fill this for each product
let dateToValues = data.map(d => [d.date, 0]);
// build map of product-id to values-for-each-date
let products = new Map();
data.forEach(d => d.products.forEach(p => {
let values = products.get(p.id)?.data;
if (values === undefined) {
values = new Map(dateToValues); // a copy
products.set(p.id, {label: p.label, data: values});
}
values.set(d.date, p.value);
}))
// generate output, skipping dates and only showing their values
let output = [];
products.forEach((v, id) => output.push({
id: id, name: v.label, data: [... v.data.values()]}));
console.log(output)

Related

Getting an array from array of array objects

I have an array which consists of an array objects as shown:
dataArr = [
{
id: 1,
arrObj: [
{
id: 11,
label: 'apple'
},
{
id: 12,
label: 'ball'
}
]
},
{
id: 2,
arrObj: [
{
id: 21,
label: 'car'
},
{
id: 22,
label: 'dog'
}
]
}
];
I need to extract an array consisting of only arrObj objects:
var newArr = [
{
id: 11,
label: 'apple'
},
{
id: 12,
label: 'ball'
},
{
id: 21,
label: 'car'
},
{
id: 22,
label: 'dog'
}
];
Tried using reduce method unsuccessfully:
dataArr.reduce((previousValue, currentValue, currentIndex, array) => {
return previousValue. arrObj.concat(currentValue.arrObj)
});
Let me know how to do this. Thanks
let dataArr = [
{
id: 1,
arrObj: [
{
id: 11,
label: 'apple'
},
{
id: 12,
label: 'ball'
}
]
},
{
id: 2,
arrObj: [
{
id: 21,
label: 'car'
},
{
id: 22,
label: 'dog'
}
]
}
];
let result = dataArr.flatMap(e => e.arrObj)
console.log(result)
You were pretty close.
There's no arrObj property in your result, it's just an array.
You need to provide an empty array as the initial value argument to reduce().
const dataArr = [{
id: 1,
arrObj: [{
id: 11,
label: 'apple'
},
{
id: 12,
label: 'ball'
}
]
},
{
id: 2,
arrObj: [{
id: 21,
label: 'car'
},
{
id: 22,
label: 'dog'
}
]
}
];
const newArr = dataArr.reduce((previousValue, currentValue) => {
return previousValue.concat(currentValue.arrObj)
}, []);
console.log(newArr);
You could use in one line by using Spread Operator...
dataArr.reduce((previousValue, currentValue) => [...previousValue?.arrObj, ...currentValue?.arrObj]);
Tip: use Optional chaining ?. in case there is no property arrObj!
You can use the Array#reduce method as follows:
let dataArr = [
{
id: 1,
arrObj: [
{
id: 11,
label: 'apple'
},
{
id: 12,
label: 'ball'
}
]
},
{
id: 2,
arrObj: [
{
id: 21,
label: 'car'
},
{
id: 22,
label: 'dog'
}
]
}
];
let newArr = dataArr.reduce((acc,cur) => [...acc, ...cur.arrObj], []);
console.log( newArr );

what is wrong in the array map iteration

The Below code, not returning a flat array, is highly confusing, Need a flat array of deeply nested array of objects
have attached jsfiddle link https://jsfiddle.net/k6swuvox/
const arr = [{
id: 1,
name: 'XYZ 1'
}, {
id: 2,
name: 'XYZ 2',
children: [{
id: 5,
name: 'XYZ 5'
}, {
id: 6,
name: 'XYZ 6',
age: 29,
children: [{
id: 7,
name: 'XYZ 7'
}, {
id: 8,
name: 'XYZ 8'
}]
}]
}, {
id: 3,
name: 'XYZ 3'
}, {
id: 4,
name: 'XYZ 4'
}]
const flats = data => data.map(e => {
if (e.children) {
return [...flats(e.children), e]
} else {
console.log("E", e);
return e
}
})
console.log(flats(arr));
With your current code, you will sometimes return an array from the callback:
return [...flats(e.children),e]
and you'll sometimes return a plain object:
else {console.log("E",e);return e }
so the result will be a mix of arrays and plain objects, instead of an array of only objects.
Use flatMap instead, which will implement the flattening you're looking for for you. You'll also need to remove the .children property from items with children before returning them.
const arr=[{id:1,name:"XYZ 1"},{id:2,name:"XYZ 2",children:[{id:5,name:"XYZ 5"},{id:6,name:"XYZ 6",age:29,children:[{id:7,name:"XYZ 7"},{id:8,name:"XYZ 8"}]}]},{id:3,name:"XYZ 3"},{id:4,name:"XYZ 4"}];
const flats = data => data.flatMap(e=>{
const { children, ...objWithoutChildren } = e;
return children
? [...flats(children), objWithoutChildren]
: e;
});
console.log(flats(arr));
Here is an iterative solution using object-scan
// const objectScan = require('object-scan');
const arr = [{ id: 1, name: 'XYZ 1' }, { id: 2, name: 'XYZ 2', children: [{ id: 5, name: 'XYZ 5' }, { id: 6, name: 'XYZ 6', age: 29, children: [{ id: 7, name: 'XYZ 7' }, { id: 8, name: 'XYZ 8' }] }] }, { id: 3, name: 'XYZ 3' }, { id: 4, name: 'XYZ 4' }];
const flatten = objectScan(['**(^children$).id'], {
useArraySelector: false,
rtn: 'parent',
reverse: false,
afterFn: (state) => {
state.result = state.result.map(({ id, name }) => ({ id, name }));
}
});
console.log(flatten(arr));
// => [ { id: 1, name: 'XYZ 1' }, { id: 2, name: 'XYZ 2' }, { id: 5, name: 'XYZ 5' }, { id: 6, name: 'XYZ 6' }, { id: 7, name: 'XYZ 7' }, { id: 8, name: 'XYZ 8' }, { id: 3, name: 'XYZ 3' }, { id: 4, name: 'XYZ 4' } ]
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/object-scan#15.0.0"></script>
Disclaimer: I'm the author of object-scan
Need a flat array of deeply nested array of objects
In that case, I'd prefer recursive to get the flexible and high deep level array.
const arr = [{id:1,name:'XYZ 1'},{id:2,name:'XYZ 2',children:[{id:5,name:'XYZ 5'},{id:6,name:'XYZ 6',age:29,children:[{id:7,name:'XYZ 7'},{id:8,name:'XYZ 8'}]}]},{id:3,name:'XYZ 3'},{id:4,name:'XYZ 4'}];
const recursive = (array) => array.reduce((acc, {children = [], ...others}) => {
acc.push(others);
if(children.length > 0) // Base recurisve here.
acc = acc.concat(recursive(children));
return acc;
}, []);
console.log(recursive(arr));
==> As a result, the content structure will make sure like this

JavaScript how to map through two different arrays of objects and return one array conditionally

hello I need help creating the newProducts in the following example array dynamically without knowing what's in the other two arrays
https://jsfiddle.net/od4267fv/
more explanation:
I would like to check
products = [
{
id: 1,
name: 'product 1' ,
liked: false
},
{
id: 2,
name: 'product 2' ,
liked: false
},
{
id: 3,
name: 'product 3' ,
liked: false
},
{
id: 4,
name: 'product 4' ,
liked: false
},
{
id: 5,
name: 'product 5' ,
liked: false
},
]
likedProducts = [
{
id: 1,
name: 'product 1' ,
liked: true
},
{
id: 2,
name: 'product 2' ,
liked: true
},
{
id: 3,
name: 'product 3' ,
liked: true
},
]
if proucts[product].id === likedProducts[product].id
newProducts.push(likedProducts[product])
else
newProducts.push(proucts[product])
please recommend suitable ways to do it
You can do the following using map() and findIndex(),
products = [
{
id: 1,
name: 'product 1' ,
liked: false
},
{
id: 2,
name: 'product 2' ,
liked: false
},
{
id: 3,
name: 'product 3' ,
liked: false
},
{
id: 4,
name: 'product 4' ,
liked: false
},
{
id: 5,
name: 'product 5' ,
liked: false
},
]
likedProducts = [
{
id: 1,
name: 'product 1' ,
liked: true
},
{
id: 2,
name: 'product 2' ,
liked: true
},
{
id: 3,
name: 'product 3' ,
liked: true
},
]
newProducts = products.map(item => {
let index = likedProducts.findIndex(likedProduct => likedProduct.id === item.id);
if(index > -1) {
return likedProducts[index];
}
return item;
})
console.log(newProducts);
This is the way to iterate over 2 array and update value upon some certain condition.
const updatedProduct = proucts.map((row, indexOfRows) => {
for (let index = 0; index < likedProducts.length; index++) {
if (likedProducts[index].id === row.id) {
return likedProducts[index];
}
}
return row;
});
You can do a single iteration over each of the arrays. First index all products by their id, then iterate over the liked products and overwrite the indexed items. This way, it doesn't matter how many items each array has, nor do you need to do constant lookups into at least one of the arrays.
const products = [{id: 1,name: 'product 1' ,liked: false},{id: 2,name: 'product 2' ,liked: false},{id: 3,name: 'product 3' ,liked: false},{id: 4,name: 'product 4' ,liked: false},{id: 5,name: 'product 5' ,liked: false},];
const likedProducts = [{id: 1,name: 'product 1' ,liked: true},{id: 2,name: 'product 2' ,liked: true},{id: 3,name: 'product 3' ,liked: true},];
const index = new Map();
//index all products
for (let item of products) {
index.set(item.id, item);
}
//overwrite anything that is liked
for (let likedItem of likedProducts) {
if (index.has(likedItem.id)) {
index.set(likedItem.id, likedItem);
}
}
//construct a new array from the values
const result = Array.from(index.values());
console.log(result);
Try this
const allProducts = products.filter(n => likedProducts.find(n2 => n.id == n2.id));

Count duplicates in an array and return new array with a key value derived from another array in Javascript

Following on from my previous question, I'd like to change and extend the capability of what was suggested.
Here's the data I've got:
const things = [
{
id: 1,
title: 'Something',
categoryId: 1
},
{
id: 2,
title: 'Another thing',
categoryId: 1
},
{
id: 3,
title: 'Yet another thing',
categoryId: 2
},
{
id: 4,
title: 'One more thing',
categoryId: 4
},
{
id: 5,
title: 'Last thing',
categoryId: 4
}
]
const categories = [
{
id: 1,
title: 'Category 1'
},
{
id: 2,
title: 'Category 2'
},
{
id: 4,
title: 'Category 3'
}
]
Previously I've been shown how to do something along these lines:
const duplicatesCountWithTitle = (things, categories) => {
const thingsReduced = things.reduce((hash, { categoryId }) => {
hash[categoryId] = (hash[categoryId] || 0) + 1
return hash
}, {})
}
As you'd be able to tell, the problem with this is that it actually returns a new object, and not a new array. Also I'd like to join the categoryTitle from the categories array with the results of the duplicated count from the things array, based on the categoryId matching the id in categories.
// currently the above returns an object in the structure of:
// {
// 1: 2,
// 2: 1,
// 4: 2
// }
// what I'm after is an array like this:
// [
// { 'Category 1': 2 },
// { 'Category 2': 1 },
// { 'Category 3': 2 }
// ]
Thanks in advance, again.
Something like this?
const newArr = categories.map(category => {
const count = things.filter(thing => thing.categoryId === category.id).length;
return { [category.title]: count }
});
console.log(newArr);
https://jsfiddle.net/f3x6m12j/
You could take a Map for the relation of id and title.
const
duplicatesCountWithTitle = (things, categories) => things.reduce((hash, { categoryId }) => {
hash[categories.get(categoryId)] = (hash[categories.get(categoryId)] || 0) + 1
return hash;
}, {}),
things = [{ id: 1, title: 'Something', categoryId: 1 }, { id: 2, title: 'Another thing', categoryId: 1 }, { id: 3, title: 'Yet another thing', categoryId: 2 }, { id: 4, title: 'One more thing', categoryId: 4 }, { id: 5, title: 'Last thing', categoryId: 4 }],
categories = [{ id: 1, title: 'Category 1' }, { id: 2, title: 'Category 2' }, { id: 4, title: 'Category 3' }],
result = duplicatesCountWithTitle(
things,
categories.reduce((m, { id, title }) => m.set(id, title), new Map)
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Get property of first objects in two arrays

I have two array of objects:
var books = [
{ id: 1, name: 'Book A' },
{ id: 2, name: 'Book B' }
];
var cars = [
{ id: 1, name: 'Car A' },
{ id: 2, name: 'Car B' },
{ id: 3, name: 'Car C' },
];
I need to create an array of strings that contains:
1. The Name of the first Book in books (if there are any)
2. The Names of the first 2 Cars in cars (if there are any)
I can do:
if (books.length > 0)
var bookA = books[0].name;
or:
if (cars.length > 1) {
var carA = cars[0].name;
var carB = cars[1].name;
}
Then build the string array but I believe there might be a better way to do this.
Can use filter() and map()
var books = [
{ id: 1, name: 'Book A' },
{ id: 2, name: 'Book B' }
];
var cars = [
{ id: 1, name: 'Car A' },
{ id: 2, name: 'Car B' },
{ id: 3, name: 'Car C' }
];
var res = [books[0], cars[0], cars[1]]
.filter(e => e)// remove undefined's
.map(({name:n}) => n)
console.log(res)
If you are using ES6. You can use [...array1,...array2] to merge them. So I slice the first item in book and use map to get a new array with only string name, and map it to result array.
For the cars array I slice the first two cars and do the same
var resultArray =[];
var books = [
{ id: 1, name: 'Book A' },
{ id: 2, name: 'Book B' }
];
var cars = [
{ id: 1, name: 'Car A' },
{ id: 2, name: 'Car B' },
{ id: 3, name: 'Car C' }
];
resultArray = [...resultArray, ...books.slice(0,1).map(v => v.name)]
resultArray = [...resultArray, ...cars.slice(0,2).map(v => v.name)]
console.log(resultArray)
One of a million ways to do it. This one would allow you to easily create a data structure (arrayDefinition) that configures what property to get from which array and at which index, which you could e.g. retrieve from a RESTful webservice.
var books = [
{ id: 1, name: 'Book A' },
{ id: 2, name: 'Book B' }
];
var cars = [
{ id: 1, name: 'Car A' },
{ id: 2, name: 'Car B' },
{ id: 3, name: 'Car C' },
];
const arrayDefinition = [
{
source: 'books',
index: 0,
prop: 'name'
},
{
source: 'cars',
index: 0,
prop: 'name'
},
{
source: 'cars',
index: 1,
prop: 'name'
}
];
let resultArr = []
arrayDefinition.forEach(function(def) {
if (Array.isArray(window[def.source]) && window[def.source].length >= def.index) {
resultArr.push(window[def.source][def.index][def.prop])
}
})
console.log(resultArr)

Categories