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)
Related
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)
I have been looking around for a JavaScript method to return the index of a value but I can't seem to find one that works.
I have the following code:
let topics = [
{
id: 1,
name: 'Topic 1',
children: [
{
id: 2,
name: 'Subtopic 1.1' <---- Searching for this value
}
]
}
];
Is there a method to use on the topics variable to search through the entire object array at once for the value of Subtopic 1.1 and then return the parent index, which in this case would be 0.
There isn't a single function, but you can nest an Array.prototype.find function inside an Array.prototype.findIndex without issue to achieve what you want (findIndex to search through the parents, find to search through the children):
let topics = [{
id: 1,
name: 'Topic 1',
children: [{
id: 2,
name: 'Subtopic 1.1' // <---- Searching for this value
}]
},
{
id: 2,
name: 'Topic 6',
children: [{
id: 5,
name: 'Subtopic 1.7'
}]
},
{
id: 3,
name: 'Topic 9',
children: [{
id: 4,
name: 'Subtopic 1.192'
},
{
id: 28,
name: 'Subtopic 999'
}],
},
];
function findParentIndex(name) {
return topics.findIndex(topic => topic.children.find(child => child.name === name));
}
console.log(findParentId("Subtopic 1.192")); // 3
console.log(findParentId("Subtopic 1.1")); // 1
console.log(findParentId("Not in the list")); // -1
You can use array.findIndex()
let topics = [{
id: 1,
name: 'Topic 1',
children: [{
id: 1,name: 'Subtopic 1.2'
}, {
id: 4,name: 'Subtopic 1.4'
}, {
id: 2, name: 'Subtopic 1.1'
}]
}];
const findIndexOf = val => {
return topics[0].children.findIndex(e => e.name.trim() === val.trim())
}
console.log(findIndexOf('Subtopic 1.1'))
No.
You would iterate through children, then have a nested loop iterating through each index value. If you find a match, the incrementing variable from the parent loop is the index you want.
edit: code example
let topics = [
{
id: 1,
name: 'Topic 1',
children: [
{
id: 2,
name: 'Subtopic 1.1'
},
{
id: 2,
name: 'Subtopic 3.1'
},
{
id: 2,
name: 'Subtopic 1.1'
},
{
id: 2,
name: 'Subtopic 2.1'
},
{
id: 2,
name: 'Subtopic 1.1'
}
]
}
];
for (let i in topics[0]["children"]) {
if (topics[0]["children"][i]["name"] == "Subtopic 1.1") {
console.log(i)
}
}
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
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; }
I have two arrays as types and defaultTypes. I need to display types with default values array defaultTypes.
const types = [
{
Id: 2,
Name: 'Some value here'
},
{
Id: 3,
Name: 'Some value here'
},
{
Id: 4,
Name: 'Some value here'
}
];
const defaultTypes = [
{
Id: 1,
Name: 'Default value 1'
},
{
Id: 2,
Name: 'Default value 2'
}
]
If in types does not exist some of default types (in this case Id: 1 does not exist in the types array). I need to add that object in types array.
Expected result will be:
const expectedTypes = [
{
Id: 1,
Name: '-'
},
{
Id: 2,
Name: 'Some value here'
},
{
Id: 3,
Name: 'Some value here'
},
{
Id: 4,
Name: 'Some value here'
}
];
Objects with Ids 1 and 2 always need to be in expectedTypes.
const expectedTypes = types.concat(
defaultTypes.filter(
type => !types.some(t => t.Id == type.Id)
));
explanation: basically what you want is types + stuff in defaultTypes that are not in types already.
Try this one:
let types = [{
Id: 2,
Name: 'Some value here'
},
{
Id: 3,
Name: 'Some value here'
},
{
Id: 4,
Name: 'Some value here'
}
];
const defaultTypes = [{
Id: 1,
Name: 'Default value 1'
},
{
Id: 2,
Name: 'Default value 2'
}
];
defaultTypes.forEach(dt => {
if (!types.some(t => t.Id === dt.Id)) {
types.push(dt);
}
});
types = types.sort((a, b) => a.Id - b.Id);
console.log(types);
Use this code and try
var _ = require('lodash');
defaultTypes.forEach(type => {
if (!_.find(types, { Id: type.Id })) {
types.push({ Id: type.Id, Name: '-' });
}
});
You can first use create a Set using map() which will contain its of elements in types.
Then loop through the defaultTypes and check if the Set contain the Id. If doesn't then push() it to types
At the end use sort() to order the elements by Id
const types = [ { Id: 2, Name: 'Some value here' }, { Id: 3, Name: 'Some value here' }, { Id: 4, Name: 'Some value here' } ];
const defaultTypes = [ { Id: 1, Name: 'Default value 1' }, { Id: 2, Name: 'Default value 2' } ]
let ids = new Set(types.map(x => x.Id));
defaultTypes.forEach(x => {
if(!ids.has(x.Id)) types.push(x)
})
types.sort((a,b) => a.Id - b.Id)
console.log(types)
Purpose Of Set
The purpose of Set is to make the time complexity liner O(n). If you don't use Set you will need to use some() on the types in each loop. So the time-complexity would be O(m*n)
Set.prototype.has has time complexity O(1)
You could reduce the elements in the wanted order with a Map and get all values as result.
const
types = [{ Id: 2, Name: 'Some value here' }, { Id: 3, Name: 'Some value here' }, { Id: 4, Name: 'Some value here' }],
defaultTypes = [{ Id: 1, Name: 'Default value 1' }, { Id: 2, Name: 'Default value 2' }],
result = Array.from([...defaultTypes, ...types]
.reduce((m, o) => m.set(o.Id, Object.assign({}, m.get(o.Id), o)), new Map)
.values()
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }