Related
I have a following array
const _array = [{id: 1, name: 'Adam'}, {id:3, name: 'Crystal'}, {id:2, name: 'Bob'}, {id: 4, name: 'Daisy'}];
How to write a single line of code in typescript to get item where name equal to Crystal from the array?
You can use array find method like following:
const _array = [
{ id: 1, name: "Adam" },
{ id: 3, name: "Crystal" },
{ id: 2, name: "Bob" },
{ id: 4, name: "Daisy" },
];
const item = _array.find((item) => item.name === "Crystal");
console.log(item);
Output
{ id: 3, name: 'Crystal' }
i neeed to merge two arrays: Categories and Products. Each product has a category object. I need to organize by category, include the category object and keep the empty categories. GroupBy function include only one parameter.
const Categories= [
{id: 1, 'name': 'category1'}
{id: 2, 'name': 'category2'},
{id: 3, 'name': 'category3'},
{id: 4, 'name': 'category4'},
]
const Products= [
{id: 1, 'name': 'product1', category: {id: 1, name: 'category1'}},
{id: 2, 'name': 'product2', category: {id: 1, name: 'category1'}},
{id: 3, 'name': 'product3', category: {id: 2, name: 'category2'}},
{id: 4, 'name': 'product4', category: {id: 2, name: 'category2'}},
]
expected result
const result = [
{
category: {id: 1, name: 'category1'},
products:[{id:1, name: 'produt1'}, {id: 2, name: 'produto1'} ]
},
{
category: {id: 2, name: 'category2'},
products:[{id:3, name: 'produt3'}, {id: 4, name: 'produto4'} ]
},
{
category: {id: 3, name: 'category3'},
products:[]
},
{
category: {id: 4, name: 'category4'},
products:[]
},
]
attempts:
for (i = 0; i < categoriesJson.length; i++) {
categoriesJson[i] = _.assign({}, categoriesJson[i], { products: [] })
for (j = 0; j < productsJson.length; j++) {
if(productsJson[j].categoryId.objectId === categoriesJson[i].objectId){
categoriesJson[i].products.push(productsJson[j])
}
}
}
Concat the Categories (formatted by to a Product format) to the Products, group by the category.id, and then map each group - category is taken from the 1st item, while products are the the items in groups, without the category, and empty items are rejected:
const Products = [{"id":1,"name":"product1","category":{"id":1,"name":"category1"}},{"id":2,"name":"product2","category":{"id":1,"name":"category1"}},{"id":3,"name":"product3","category":{"id":2,"name":"category2"}},{"id":4,"name":"product4","category":{"id":2,"name":"category2"}}]
const Categories = [{"id":1,"name":"category1"},{"id":2,"name":"category2"},{"id":3,"name":"category3"},{"id":4,"name":"category4"}]
const result = _(Products)
.concat(Categories.map(category => ({ category })))
.groupBy('category.id')
.map(group => ({
category: _.head(group).category,
products: _(group)
.map(o => _.omit(o, 'category'))
.reject(_.isEmpty)
.value()
}))
.value()
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.js"></script>
And the same idea with lodash/fp. Wrap the _.flow() with the _.useWith() function, and preformat the Categories (2nd param) to fit the Categories. The rest is similar to the lodash chain.
const { useWith, identity, flow, concat, groupBy, map, head, omit, reject, isEmpty } = _
const formatProducts = flow(map(omit('category')), reject(isEmpty))
const fn = useWith(flow(
concat,
groupBy('category.id'),
map(group => ({
category: head(group).category,
products: formatProducts(group)
}))
), [identity, map(category => ({ category }))])
const Products = [{"id":1,"name":"product1","category":{"id":1,"name":"category1"}},{"id":2,"name":"product2","category":{"id":1,"name":"category1"}},{"id":3,"name":"product3","category":{"id":2,"name":"category2"}},{"id":4,"name":"product4","category":{"id":2,"name":"category2"}}]
const Categories = [{"id":1,"name":"category1"},{"id":2,"name":"category2"},{"id":3,"name":"category3"},{"id":4,"name":"category4"}]
const result = fn(Products, Categories)
console.log(result)
<script src='https://cdn.jsdelivr.net/g/lodash#4(lodash.min.js+lodash.fp.min.js)'></script>
If lodash is not a requirement in the solution, this is how I did it with plain javascript;
const Categories= [
{id: 1, 'name': 'category1'},
{id: 2, 'name': 'category2'},
{id: 3, 'name': 'category3'},
{id: 4, 'name': 'category4'}
];
const Products= [
{id: 1, 'name': 'product1', category: {id: 1, name: 'category1'}},
{id: 2, 'name': 'product2', category: {id: 1, name: 'category1'}},
{id: 3, 'name': 'product3', category: {id: 2, name: 'category2'}},
{id: 4, 'name': 'product4', category: {id: 2, name: 'category2'}},
];
const result = [];
for (let index in Categories) {
let category_id = Categories[index].id;
result.push({
category: Categories[index],
products: GetProductsWithCategoryId(category_id)
});
}
function GetProductsWithCategoryId(category_id) {
let products = [];
for (let index in Products) {
if (Products[index].category.id == category_id) {
products.push({
id: Products[index].id,
name: Products[index].name
});
}
}
return products;
}
console.log("result:", result);
Using reduce, create a mappedProducts object which groups the Products based on the category.id. Like this:
{
"1": [{ id: 1, name: "product1" }, { id: 2, name: "product2" }],
"2": [{ id: 3, name: "product3" }, { id: 4, name: "product4" }]
}
Then, map the Categories array and get the output for each category
const Categories=[{id:1,name:"category1"},{id:2,name:"category2"},{id:3,name:"category3"},{id:4,name:"category4"},],
Products=[{id:1,name:"product1",category:{id:1,name:"category1"}},{id:2,name:"product2",category:{id:1,name:"category1"}},{id:3,name:"product3",category:{id:2,name:"category2"}},{id:4,name:"product4",category:{id:2,name:"category2"}}];
const mappedProducts = Products.reduce((acc, { category, ...rest }) => {
acc[category.id] = acc[category.id] || [];
acc[category.id].push(rest)
return acc;
}, {})
const output = Categories.map(category => ({
category,
products: mappedProducts[category.id] || []
}))
console.log(output)
In a single function. Lodash is not necessary:
const Categories = [
{ id: 1, name: "category1" },
{ id: 2, name: "category2" },
{ id: 3, name: "category3" },
{ id: 4, name: "category4" }
];
const Products = [
{ id: 1, name: "product1", category: { id: 1, name: "category1" } },
{ id: 2, name: "product2", category: { id: 1, name: "category1" } },
{ id: 3, name: "product3", category: { id: 2, name: "category2" } },
{ id: 4, name: "product4", category: { id: 2, name: "category2" } }
];
function combine(categories, products) {
return categories.reduce((list, category) => {
const nextItem = {
category,
products: [
products.filter(p => p.category.id === category.id).map(
({ id, name }) => ({
id,
name
})
)
]
};
list.push(nextItem);
return list;
}, []);
}
const result = combine(Categories, Products)
Now for your information, if you had a huge list of categories and/or products, this wouldn't be the ideal solution as there is a lot of looping involved. Instead, you would cache products in such a way that you only ever need to look at a given product once (rather than looking at every product for every category). With a small data set, this optimization isn't necessary.
I am making a call to two end points and need to display all the companies with their funds,name and factory that produces for that company.
here is the response from one end point
let factories = [
{
id: 1,
name: "Xintang",
short: "xin",
companies: [0, 4, 101,198]
},
{
id: 2,
name: "Ohio Plant",
short: "OHP",
companies: [22, 27]
},
{
id: 3,
name: "Cincy",
short: "Cin",
companies: []
}
];
Here is the response from the second
let companies = [
{
id: 0,
fund: "79588.96",
name: "Microsoft"
},
{
id: 1,
fund: "166727.06",
name: "Comcast"
},
{
id: 2,
fund: "131206.88",
name: "Apple"
},
{
id: 3,
fund: "74095.75",
name: "HP"
},
{
id: 4,
fund: "142556.86",
name: "Dell"
}
];
the dataset is much bigger, but here is just a sample. So I want be able to create a new object that links the factory with the specific company. Is there a way I can map over the companies and check which factory has the company id in that nested array so that I can add a new property factory to the company, and have a new array of objects that would look like this.
let returnedArr = [
{
id: 0,
fund: "79588.96",
name: "Microsoft",
factory: "Xintang"
},
{
id: 4,
fund: "142556.86",
name: "Dell",
factory: "Xintang"
}
];
You can do the following using reduce and Map.
Get the company-id and factory-name Map -> Then loop through the companies and create the output
let factories = [{id:1,name:"Xintang",short:"xin",companies:[0,4,101,198]},{id:2,name:"Ohio Plant",short:"OHP",companies:[22,27]},{id:3,name:"Cincy",short:"Cin",companies:[]}],
companies = [{id:0,fund:"79588.96",name:"Microsoft"},{id:1,fund:"166727.06",name:"Comcast"},{id:2,fund:"131206.88",name:"Apple"},{id:3,fund:"74095.75",name:"HP"},{id:4,fund:"142556.86",name:"Dell"}]
/*Get the company id: factory name mapping*/
const map = factories.reduce((m, f) =>
(f.companies.forEach(c => m.set(c, f.name)), m)
, new Map);
const output = companies.map(c => ({...c, factory: map.get(c.id) || ''}));
console.log(output)
Try This.... It may help u...
let result = [];
companies.forEach(company => {
let tempCompany = {...company};
factories.forEach(factory => {
let tempArray = factory.companies.filter(item => item === company.id);
if(tempArray.length > 0) {
tempCompany.factory = factory.name;
}
});
result.push(tempCompany);
});
One way to do this is to create a map of company ids to factory ids, then just iterate through your companies array and add the corresponding factory to the company object, like so:
The big advantage of this is that your factoryid lookups will be O(1), and it is O(n) to build the map. Your entire algorithm will be O(n). This makes this extremely fast even for very large data sets.
let factories = [
{
id: 1,
name: "Xintang",
short: "xin",
companies: [0, 4, 101,198]
},
{
id: 2,
name: "Ohio Plant",
short: "OHP",
companies: [22, 27]
},
{
id: 3,
name: "Cincy",
short: "Cin",
companies: []
}
];
let companies = [
{
id: 0,
fund: "79588.96",
name: "Microsoft"
},
{
id: 1,
fund: "166727.06",
name: "Comcast"
},
{
id: 2,
fund: "131206.88",
name: "Apple"
},
{
id: 3,
fund: "74095.75",
name: "HP"
},
{
id: 4,
fund: "142556.86",
name: "Dell"
}
];
var factoryMap = factories.reduce((res, curr) => {
return Object.assign(res, curr.companies.reduce((_res, _curr) => (_res[_curr] = curr.name, res), {}))
}, {});
var mappedCompanies = companies.map(company => Object.assign(company, {factory: factoryMap[company.id] || ""}));
console.log(mappedCompanies);
Assuming that a company can have more than one factory.
Try the following
let factories = [{
id: 1,
name: "Xintang",
short: "xin",
companies: [0, 4, 101, 198]
},
{
id: 2,
name: "Ohio Plant",
short: "OHP",
companies: [22, 27]
},
{
id: 3,
name: "Cincy",
short: "Cin",
companies: []
}
];
let companies = [{
id: 0,
fund: "79588.96",
name: "Microsoft"
},
{
id: 1,
fund: "166727.06",
name: "Comcast"
},
{
id: 2,
fund: "131206.88",
name: "Apple"
},
{
id: 3,
fund: "74095.75",
name: "HP"
},
{
id: 4,
fund: "142556.86",
name: "Dell"
}
];
let returnedArr = companies.map(company => {
company.factories = factories
.filter(factory => factory.companies.includes(company.id))
.map(factory => factory.name);
return company;
});
console.log(JSON.stringify(returnedArr, null, 4));
For my React.js project I would like to create a search-filter of a nested Array. Users will search with an input-field.
var dataExample = [
{
type: "human", details: [
{id: 1, name: "Peter", description: "friendly, black-hair"},
{id: 5, name: "Susan", description: "blond"}
]
},
{
type: "animal", details: [
{id: 2, name: "Will", description: "lazy, cute"},
{id: 3, name: "Bonny", description: "beautiful"}
]
}
];
In my search-input-field I want to look for "name" or something in "description". The data structure of the array should remain the same.
The output when I'm searching for "friendly" or "Peter" should be:
[
{
type: "human", details: [
{id: 1, name: "Peter", description: "friendly, black-hair"}
]
}
];
Now I tried something like this:
let myfilter = dataExample.filter((data) => {
data.details.filter((items) => {
return (items.type.indexOf("human") !== -1 || //input of user
items.description.indexOf("friendly"))
})
})
Unfortunately, this is not how it works. Can anybody help me? Lodash would be no problem, too. Thank you so much.
You can use array#reduce with array#filter and to check for your word you can use string#incldues.
const dataExample = [ { type: "human", details: [ {id: 1, name: "Peter", description: "friendly, black-hair"}, {id: 5, name: "Susan", description: "blond"} ] }, { type: "animal",details: [ {id: 2, name: "Will", description: "lazy, cute"}, {id: 3, name: "Bonny", description: "beautiful"} ] } ],
term = 'Peter',
result = dataExample.reduce((r, {type,details}) => {
let o = details.filter(({name,description}) => name.includes(term) || description.includes(term));
if(o && o.length)
r.push({type, details : [...o]});
return r;
},[]);
console.log(result);
Here are some examples without lodash.
var dataAll = [
{
type: "human",
details: [
{id: 1, name: "Peter", description: "friendly, black-hair"},
{id: 5, name: "Susan", description: "blond"}
]
},
{
type: "animal",
details: [
{id: 2, name: "Will", description: "lazy, cute"},
{id: 3, name: "Bonny", description: "beautiful"}
]
}
];
var entryTypeFilter = data => data.type.indexOf("hum") !== -1;
var entryDetailDescFilter = data => data.description.indexOf("friend") !== -1;
var entryDetailsMapper = data => {
return {
type: data.type,
details: data.details.filter(entryDetailDescFilter)
};
};
var entryNoDetailsFilter = data => data.details && data.details.length !== 0;
var dataFilteredByType = dataAll.filter(entryTypeFilter);
var dataFilteredByDesc = dataAll.map(entryDetailsMapper);
var dataFilteredByTypeAndDesc = dataAll.filter(entryTypeFilter).map(entryDetailsMapper);
var dataFilteredByDescTrimmingEmptyDetailEntries = dataAll.map(entryDetailsMapper).filter(entryNoDetailsFilter);
In modern javascript you might want to search on how to use the ... keyword for the mapping callback functions.
I have an array that I want to transform into an object. For example:
const arr = [{id: 1, key: ''}, {id: 2, key: ''}];
I want the result to be:
const object = { 1: {id: 1, key: ''}, 2: { id: 2, key: ''}}
With lodash I can use the keyBy function, but I am working with ramda and did not find this functionality there.
In case anyone still finds this via search, the correct answer is indexBy, added in mid-2016.
const list = [
{ id: "xyz", title: "A" },
{ id: "abc", title: "B" },
];
R.indexBy(R.prop("id"), list);
//=> {abc: {id: 'abc', title: 'B'}, xyz: {id: 'xyz', title: 'A'}}
See Also:
Github: Ramda Wiki "What Ramda Function Should I Use?"
Github: Ramda Issue #931 - "sister to groupBy"
You can solve this with very basic reduce function.
function keyBy(entities, id = "id") {
return entities.reduce((acc, entity) => {
acc[entity[id]] = entity;
return acc;
}, {});
}