I am attempting to group an array elements ( containing orders details ).
here is my array structure :
[{"id":"myid","base":{"brands":["KI", "SA"],"country":"BG","status":"new"}},{"id":"DEC-00500331","base":{"brands":["DEC"],"country":"UK","status":"new"},"psp":{"name":"adyen","status":"paid"}}]
An order is related to a country website, and can contain one or more brands. for example in one order I can have and item from brand1 and an item from brand2.
I need to group these orders, by country and brand so I can have a consolidated array or object.
I can group by country easily :
let groupedDataByCountryAndBrand = _.groupBy(orders.value, 'base.country')
Object.keys(groupedDataByCountryAndBrand).forEach(key => {
table.push(
{
country : key, //to be reviewd : for the two brands or more in one order
new : groupedDataByCountryAndBrand[key].filter( order => (order.base.status === SFCC_STATUS.new
|| SFCC_STATUS.open
|| SFCC_STATUS.completed )).length
})
})
Here is the result :
Unfortunatly this is not working for me. I need to group the orders by country and brand so that I can count the newly created orders for each brand by country.
Result I am expecting is something like this :
{
"country" : "FR",
"brand": "adidas",
"pending": 4
"new" : 3,
"an other status": 5
}
Do you have any idea how I can achieve this ?
I am using lodash with vue component.
Thanks.
Here is what you are looking for, without using of any additional libraries:
const data = [{"id":"myid","base":{"orderNumber":"0500332","marketPlaceOrderCode":"","creationDate":"2022-06-14T10:49:10Z","source":"sfcc","brands":["KI", "SA"],"country":"BG","status":"new","totalEuro":12,"currency":"BGN","units":1,"coupons":null,"shipping":{"id":"BG01","name":"Speedy COD","status":"not_shipped"}},"psp":{"name":"payu","method":null,"status":null},"tms":null},{"id":"DEC-00500331","base":{"orderNumber":"id2","marketPlaceOrderCode":"","creationDate":"2022-06-14T10:41:29Z","source":"sfcc","brands":["DEC"],"country":"UK","status":"new","totalEuro":57,"currency":"GBP","units":1,"coupons":null,"shipping":{"id":"ECA_DPD_ST","name":"Standard shipping","status":"not_shipped"}},"psp":{"name":"adyen","method":null,"status":"paid"},"tms":null}];
const rawResult = normalizeData(data);
console.log(makeItReadable(rawResult));
function normalizeData(data) {
const result = {};
data.forEach((order) => {
const orderData = order.base;
// add country to result (if not exists)
if (!result[orderData.country]) {
result[orderData.country] = {};
}
orderData.brands.forEach((brand) => {
// add brand to exact country (if not exists)
if (!result[orderData.country][brand]) {
result[orderData.country][brand] = {};
}
// add status to exact brand and country (if not exists)
if (!result[orderData.country][brand][orderData.status]) {
result[orderData.country][brand][orderData.status] = 0;
}
// increment status count
++result[orderData.country][brand][orderData.status];
});
});
return result;
}
function makeItReadable(rawData) {
const readableResult = [];
Object.keys(rawData).map((country) => {
Object.keys(rawData[country]).map((brand) => {
readableResult.push({country, brand, ...rawData[country][brand]});
});
});
return readableResult;
}
This code will give you the following result:
[
{ country: 'BG', brand: 'KI', new: 1 },
{ country: 'BG', brand: 'SA', new: 1 },
{ country: 'UK', brand: 'DEC', new: 1 }
]
Related
I am building a React app that includes one separate component for CRUD functionality of Products and another separate component for CRUD functionality of Suppliers.
I am using the same saveData method for both components (the Create functionality of CRUD.. that is triggered when the User presses Save after filling in the input fields of Product or Supplier). The saveData method is located in a central ProductsAndSuppliers.js file that is available to both the Products and Supplier components.
In both of the Product & Supplier components, there is a table showing the Products or Suppliers already present as dummy data.
I made a button at the bottom of each page to add a new Product or Supplier... depending on which tab the user has selected on the left side of the screen (Product or Supplier).
Since I am using the same saveData method in both cases, I have the same problem whenever I try to add a new Product or Supplier to each respective table after filling out the input fields. My new Product or Supplier is added.. but twice and I can't figure out why.
I have tried using a spread operator to add the new item to the collection but am having no success:
saveData = (collection, item) => {
if (item.id === "") {
item.id = this.idCounter++;
this.setState((collection) => {
return { ...collection, item }
})
} else {
this.setState(state => state[collection]
= state[collection].map(stored =>
stored.id === item.id ? item : stored))
}
}
Here is my original saveData method that adds the new Product or Supplier, but twice:
saveData = (collection, item) => {
if (item.id === "") {
item.id = this.idCounter++;
this.setState(state => state[collection]
= state[collection].concat(item));
} else {
this.setState(state => state[collection]
= state[collection].map(stored =>
stored.id === item.id ? item : stored))
}
}
my state looks like this:
this.state = {
products: [
{ id: 1, name: "Kayak", category: "Watersports", price: 275 },
{ id: 2, name: "Lifejacket", category: "Watersports", price: 48.95 },
{ id: 3, name: "Soccer Ball", category: "Soccer", price: 19.50 },
],
suppliers: [
{ id: 1, name: "Surf Dudes", city: "San Jose", products: [1, 2] },
{ id: 2, name: "Field Supplies", city: "New York", products: [3] },
]
}
There are issues with both of your implementations.
Starting with the top one:
// don't do this
this.setState((collection) => {
return { ...collection, item }
})
In this case, collection is your component state and you're adding a property called item to it. You're going to get this as a result:
{
products: [],
suppliers: [],
item: item
}
The correct way to do this with the spread operator is to return an object that represents the state update. You can use a computed property name to target the appropriate collection:
this.setState((state) => ({
[collection]: [...state[collection], item]
})
)
* Note that both this and the example below are using the implicit return feature of arrow functions. Note the parens around the object.
In the second code sample you're
mutating the existing state directly which you should not do.
returning an array instead of a state update object.
// don't do this
this.setState(state =>
state[collection] = state[collection].concat(item)
);
Assignment expressions return the assigned value, so this code returns an array instead of an object and I'd frankly be surprised if this worked at all.
The correct implementation is the same as above except it uses concat instead of spread to create the new array:
this.setState(state => ({
[collection]: state[collection].concat(item)
})
);
needlessly fancy, arguably silly id generators:
const nextId = (function idGen (start = 100) {
let current = start;
return () => current++;
})(100);
console.log(nextId()); // 100
console.log(nextId()); // 101
console.log(nextId()); // 102
// ----------------
// a literal generator, just for fun
const ids = (function* IdGenerator(start = 300) {
let id = start;
while (true) {
yield id++;
}
})();
console.log(ids.next().value); // 300
console.log(ids.next().value); // 301
console.log(ids.next().value); // 302
I'm having two data arrays which are coming from API and sample arrays would be like this
Array 1
[
{userId: 1
description: "Student"
imagePath: "test.png"
status: 1
}]
Array 2
[
{id: 85
accountName: "Rahul"
accountNumber: "11145678"
}
]
In my reactnative app view there's search bar and user should be able to search from these two arrays. So I merged these two arrays into one using
this.searchArray =this.filterArray[0].concat(this.filterArray[1])
So, my searchArray is a single array with Array1 and Array2 data. sample below
[
{userId: 1
description: "Student"
imagePath: "test.png"
status: 1
},
{id: 85
accountName: "Rahul"
accountNumber: "11145678"
}]
My search function is below (I need to search from account number or description)
//Search Filter
searchFilter =searchText=>{
const searchTextData = searchText.toUpperCase();
const userSearch = this.searchArray.filter(item => {
const itemData = `${item.description && item.description.toUpperCase()} ${item. accountName && item.accountName.toUpperCase()}`;
return itemData.indexOf(searchTextData) > -1;
});
}
The search functionality is not working with accountName. It's not getting any results. But if I remove ${item. accountName && item.accountName.toUpperCase()} , then it's working showing data with description. But I need to filter from both
In your array one object can have description or accountNumber so do a check if that exists include it in the itemData variable.
Try doing this
searchFilter =searchText=>{
const searchTextData = searchText.toUpperCase();
const userSearch = this.searchArray.filter(item => {
const itemData = `${item.hasOwnProperty('description'))?item.description.toUpperCase():''} ${item.hasOwnProperty('accountNumber')?item.accountNumber:''}`;
return itemData.indexOf(searchTextData) > -1;
});
}
First merge the two objects into one:
Object.keys(arr2[0]).forEach(key => {
arr1[0][key] = arr2[0][key]
})
Then create the search function:
function searchObject(obj, value){
return Object.keys(obj).some(key => {
return obj[key] === value
})
}
let arr1=[{userId:1,description:"Student",imagePath:"test.png",status:1}],arr2=[{id:85,accountName:"Rahul",accountNumber:"11145678"}];
Object.keys(arr2[0]).forEach(key => {
arr1[0][key] = arr2[0][key]
})
function searchObject(obj, prop, value){
return obj[prop] === value
}
console.log(searchObject(arr1[0], "accountName", "asdf"))
console.log(searchObject(arr1[0], "accountName", "Rahul"))
I'm sure, this is a simple question, but actually I can't find a solution. Maybe someone can give me hint.
I have this type of Object array:
const Country = {
albania:
{
'iso':'al',
'countryNo': 70,
'name':
{
de: 'Albanien',
en: 'Albania',
pl: 'Albania',
},
'flag':'flag-icon-al',
'showCountry':true,
},
austria:
{
'iso':'at',
'countryNo': 38,
'name':
{
de: 'Österreich',
en: 'Austria',
pl: 'Austria',
},
'flag':'flag-icon-at',
'showCountry':true,
},
belgium:
{
'iso':'be',
'countryNo': 2,
'name':
{
de: 'Belgien',
en: 'Belgium',
pl: 'Belgia',
},
'flag':'flag-icon-be',
'showCountry':true,
},
...
The keys of this object are albania, austria, etc.
Now I want to sort the array. This I'm doing with lodash sortBy function.
let countryList = _.sortBy(Country,'name[' + this.props.currentLanguage + ']');
When I iterate through the countryList array, how can I get the original keys from the Country object array, i.e. albania?
I tried to work with map function, but then I only get keys named 0, 1 etc.
countryList.map((country,key) => {
// country -> has all object information
// key -> 0, 1, 2 etc.
})
See the debug picture:
UPDATE
Is there any better solution than this:
countryList.map((country,key) => {
var key = Object.keys(Country).find(function(element) {
const countryInner = Country[element];
if(countryInner.countryNo === country.countryNo) {
return element;
}
});
if(country.showCountry === true) {
return (
<HashLink to="/#locations" className={"dropdown-item imageLink animated"} onClick={(e) => this.handleClickCountry(e, key)} key={"nav" + key}>{country.name[this.props.currentLanguage].toUpperCase()}</HashLink>
);
}
})
The solution i will suggest is instead of using lodash , create a 2d array out of your object.Basically move them to an array, sort that array and then rebuild
var countryList = [];
for (var con in Country) {
countryList.push([con, Country[con]]);
}
//Sort based on key value
sortable.sort(function(a, b) {
return a[0] > b[0];
});
Once you have the array, you could rebuild the object from the array in the order you like. By this your key wont be lost and you don't have to iterate twice to find that.
Hope it helps
When you sort your collection you will lose your object keys, so if it is important for you to keep them after sorting you can add these keys into objects as property you can do it with map
let countries = _.map(Country, (country, key) => {
country.key = key;
return country;
});
Now we can sort array again with sortBy method of lodash
let orderedCountries = _.sortBy(countries, function (country) {
return country.name[lang]
});
where lang is one of your language ('de'm 'en', 'pl')...
I believe you also want to filter your list depends on their showCountry property so lets merge all of them...
function sortAndFilterCountries(countries, lang) {
countries = _.map(countries, (country, key) => {
country.key = key;
return country;
});
let orderedCountries = _.sortBy(countries, function (country) {
return country.name[lang]
});
return _.filter(orderedCountries, 'showCountry');
}
I will be learning vuejs for the past few days. I have to apply search filter for particular column. In the grid component example given in vuejs documentation it searches all column.
Tutorial Link https://v2.vuejs.org/v2/examples/grid-component.html
gridData: [
{ name: 'Chuck Norris', power: Infinity },
{ name: 'Bruce Lee', power: 9000 },
{ name: 'Jackie Chan', power: 7000 },
{ name: 'Jet Li', power: 8000 }
]
I have to apply search only for name column. Can someone please help me to fix it. Here is the working fiddle
Just change your computed property filteredData in this way:
computed: {
filteredData: function () {
var sortKey = this.sortKey
var filterKey = this.filterKey && this.filterKey.toLowerCase()
var order = this.sortOrders[sortKey] || 1
var data = this.data
// FILTER ONLY BY NAME
if (filterKey) {
data = data.filter( function (row) {
return row.name.toLowerCase().includes(filterKey)
})
}
// ==============
if (sortKey) {
data = data.slice().sort(function (a, b) {
a = a[sortKey]
b = b[sortKey]
return (a === b ? 0 : a > b ? 1 : -1) * order
})
}
return data
}
},
Here's the working jsfiddle
made some changes in your computed property and used an array( if you want to apply search filter in more than one column).
P.S you just need to replace code for your filtering and make an array "searchableKeys"
const searchableKeys = ['name','number']
if (filterKey) {
data = data.filter(row =>
searchableKeys.some((key) => {
const value = String(row[key])
return value.toLowerCase().indexOf(filterKey) !== -1;
}));
}
Here is the working jsfiddle in which filtering is done using 'name' and 'number'
https://jsfiddle.net/s63nfx7r/1/
This question already has answers here:
Group array items using object
(19 answers)
Closed 6 years ago.
I'm trying to group the raw data from:
items:
[
{
category: "blog",
id : "586ba9f3a36b129f1336ed38",
content : "foo, bar!"
},
{
category: "blog",
id : "586ba9f3a36b129f1336ed3c",
content : "hello, world!"
},
{
category: "music",
id : "586ba9a6dfjb129f1332ldab",
content : "wow, shamwow!"
},
]
to
[
{
category: "blog",
items:
[
{
id : "586ba9f3a36b129f1336ed38",
content : "foo, bar!"
},
{
id : "586ba9f3a36b129f1336ed3c",
content : "hello, world!"
},
]
},
{
category: "music",
items:
[
{
id : "586ba9a6dfjb129f1332ldab",
content : "wow, shamwow!"
}
]
}
]
The format like this helps me to print the same category data together in the frontend.
The content of the category field is dynamically, so I'm not sure how do I store it to a temporary object and sort them, any thoughts?
(I can't think a better title for the question, please edit if you got a better title.)
You can do it using Array#reduce in one pass:
var items = [{"category":"blog","id":"586ba9f3a36b129f1336ed38","content":"foo, bar!"},{"category":"blog","id":"586ba9f3a36b129f1336ed3c","content":"hello, world!"},{"category":"music","id":"586ba9a6dfjb129f1332ldab","content":"wow, shamwow!"}];
var result = items.reduce(function(r, item) {
var current = r.hash[item.category];
if(!current) {
current = r.hash[item.category] = {
category: item.category,
items: []
};
r.arr.push(current);
}
current.items.push({
id: item.id,
content: item.content
});
return r;
}, { hash: {}, arr: [] }).arr;
console.log(result);
Or the ES6 way using Map:
const items = [{"category":"blog","id":"586ba9f3a36b129f1336ed38","content":"foo, bar!"},{"category":"blog","id":"586ba9f3a36b129f1336ed3c","content":"hello, world!"},{"category":"music","id":"586ba9a6dfjb129f1332ldab","content":"wow, shamwow!"}];
const result = [...items.reduce((r, { category, id, content }) => {
r.has(category) || r.set(category, {
category,
items: []
});
r.get(category).items.push({ id, content });
return r;
}, new Map).values()];
console.log(result);
Personally, without any helper libraries, I'd just do this
var step1 = items.reduce((result, {category, id, content}) => {
result[category] = result[category] || [];
result[category].push({id, content});
return result;
}, {});
var result = Object.keys(step1).map(category => ({category, items: step1[category]}));
Which babel converts to
var step1 = items.reduce(function (result, _ref) {
var category = _ref.category,
id = _ref.id,
content = _ref.content;
result[category] = result[category] || [];
result[category].push({ id: id, content: content });
return result;
}, {});
var result = Object.keys(step1).map(function (category) {
return { category: category, items: step1[category] };
});
So I just solved the question with the following code (jsfiddle):
// Items
// var items = []
// Create an empty object, used to store the different categories.
var temporaryObject = {}
// Scan for each of the objects in the `items` array.
items.forEach((item) =>
{
// Create a category in the teporary object if the category
// hasn't been created.
if(typeof temporaryObject[item.category] === "undefined")
temporaryObject[item.category] = []
// Push the item to the its category of the `temporaryObject`.
temporaryObject[item.category].push({
id : item.id,
content: item.content
})
})
// Create a empty array used to stores the sorted, grouped items.
var newItems = []
// Scan for each of the category in the `temporaryObject`
for(var category in temporaryObject)
{
// Push the new category in the `newItems` array.
newItems.push({
category: category,
items : []
})
// Get the last category index of the `newItems` array,
// so we can push the related data to the related category.
var lastItem = newItems.length - 1
// Scan for the related category in the `temporaryObject` object.
temporaryObject[category].forEach((item) =>
{
// Push the related data to the related category of the `newItems` array.
newItems[lastItem].items.push(item)
})
}
// Prints the sorted, grouped result.
console.log(newItems)