I'm stuck with the code. I actually want to filter elements in dataLayer using typescript/javascript.
I have dataLayer defined as shown below
track: { products },
dataLayers: { current: { cart: { items } } }
products?: IProduct[]
export interface IProduct {
id: string
quantity?: string
name?: string
}
items?: ICartItem[]
export interface ICartItem {
id: string
brand: string
name: string
quantity: number
}
track: { products }
products have {id,quantity}
dataLayers: { current: { cart: { items } } }
items have {id, brand, name, quantity }
Now I want to filter id and get the name of the product, For Example:
Example:
*
products:{
[{id: 'a123',quantity: '1'},{id:'a345', quantity:'2'}]
}
items:{
[{id: 'a123',brand:'pen',name: 'Reynolds', quantity: '1'}, {id: 'a143',brand:'pencil',name: 'Nataraj', quantity: '3'}, {id: 'a122',brand:'pen',name: 'Parker',quantity: '1'},{id:'a345',brand:'Eraser',name: 'Faber-Castell', quantity:'2'}]
}*
Expected output
id:a123,name:'Reynolds'
id:a345,name:'Faber-Castell'
my code:
const id = products.map(product => { return product.id})
items.filter((item) => {
return item.id === id
})
.map((item)=> {
const { id, name } = item
console.log("id" + id)
console.log("name" + name)
})
Actual output
Giving error
**
const id: string[]
This condition will always return 'false' since the types 'string' and 'string[]' have no overlap.ts(2367)
**
Why am I not able to compare item.id with id
You are comparing the string array id with each item id which is a string. Do like below.
items.filter(item => id.includes(item.id))
In the first line of your code, you are fetching the array of ids from products array. Hence id is nothing but an array.
const id: string[] = products.map(product => { return product.id })
Therefore, the below code will do the job.
items.filter((item) => id.includes(item.id) )
Also, If you are coding in typescript, develop the habit of using variable with its types. [number, string, boolean, string[] ...]
Related
I am using Zustand in a Next.js app using TypeScript,
For some reason, I get a runtime error message whenever I try to iterate on my state object.
The structure of my car damaged zone object is:
const damagedZones = {
Left: {
front_door:[
{
id: 1,
picture1: 'picture 1 base64 string',
picture2: 'picture 2 base64 string',
comment: 'any comment'
},
{
id: 2,
picture1: 'picture 1 base64 string',
picture2: 'picture 2 base64 string',
comment: 'any comment'
}
],
back_door: [
],
so, imagine I add a new object to my "front_door" array, here is my zustand store and function:
In the code below, the dynamic prop "zone" would be my "Left" key of my damagedZones object, and the dynamic prop "element" would be my "front_door" key.
export const useDamagedZones = create<DamagedZonesProps>((set) => ({
damagedZones: damagedZones,
setDamagedZones: (elementItem: damagedItem, zone: string, element: string) => {
set(state => ({
damagedZones: {
...state.damagedZones,
[zone]: {
...state.damagedZones[zone],
[element]: [
...state.damagedZones[zone]?.[element],
elementItem
]
}
}
}))
},
}))
so basically when I trigger this function, I get a runtime error which says:
TypeError: Invalid attempt to spread non-iterable instance.
In order to be iterable, non-array objects must have a Symbol.iterator method.
I am not understanding why that is so....
I have tried using an object instead of an array, with an id as key, and it works fine, but it's not super convenient, so an array is the best in this situation, but it doesn't perform as expected....
Alright i figured it out, it is because i misconceived my typescript types for my damagedZone object ! i forgot to mention that one key would be an array :)
it works now that my object type is like so :
type damagedItem = {
id?: number
picture1?: string | null,
picture2?: string | null,
picture3?: string | null,
comment?: string,
}
type DamagedElement = {
[key: string]: damagedItem[]
}
type DamagedZone = {
step1_gauche: DamagedElement,
step1_droite: DamagedElement,
step1_avant: DamagedElement,
step1_arriere: DamagedElement
}
and here is the store useDamagedZones for now :
export const useDamagedZones = create<DamagedZonesProps>((set) => ({
damagedZones: damagedZones,
setDamagedZones: (elementItem: damagedItem, zone: string, element: string) => {
set(state => ({
damagedZones: {
...state.damagedZones,
[zone]: {
...state.damagedZones[zone],
[element]: [
...state.damagedZones[zone]?.[element],
elementItem
]
}
}
}))
},
removeDamagedItem : (zone: string, element: string, id: number ) => {
set(state => ({
damagedZones: {
...state.damagedZones,
[zone]: {
...state.damagedZones[zone],
[element]:
state.damagedZones[zone]?.[element].filter(el => el.id !== id)
}
}
}))
}
}))
this is my code
const [state, setState] = useState(
[{id: 1, key:""}, {id: 2, key:""}, {id: 3, key:""}]
)
i want to to change "key" state
im confuse
now im using
setState(
[...state].map((data, index) => {
if (data.id === state[index].id) {
return {
...data,
key: result,
};
} else return data;
}),
);
}
result variable came from result when i fetching data.
result is a random string
If your data structure is always going to be in that order data.id === state[index].id doesn't really achieve much.
For example:
when data.id is 1 the index will be 0. And state[0].id is 1.
when data.id is 2 the index will be 2. And state[1].id is 2.
etc.
It just sounds like you want to iterate over all the objects in state and update each key value with that random string you mentioned in the comment section. There's no need to make a copy of state since map already returns a new array ready for setState to use.
function setState(mapped) {
console.log(mapped);
}
const state = [{ id: 1, key: '' }, { id: 2, key: '' }, { id: 3, key: '' }];
const result = 'random';
const mapped = state.map(data => {
return { ...data, key: result };
});
setState(mapped);
I'm attempting to manipulate this data in react with graphql. As you can see, I have this data or input in the graphql playground, and this is how I wanted the input to look:
In my frontend, I have a cartItem with an objects inside and array, including the product name, id, and so on. I wanted the input to look like the example I provided above. Is there a way to make that happen?
Codes and Data
This is how my cart's Item Data looks.
CartItem Data:
[
{
id: "6109401fd86d352a70e3694e",
name: "asasasasa",
sku: "sasa",
shippingTime: "1628812800000",
quantity: 1,
},
{
id: "61051c14f25d8830a8e238c0",
name: "Pringles Sour Cream & Onion Potato Crisps 158g",
sku: "sad89f79dsafs",
shippingTime: "1627084800000",
quantity: 1,
},
];
As stated in the preceding example, all I wanted was the product's id and quantity.
Order.js
const [cartItems, setCartItems] = useContext(CartContext);
const [createOrder, { data, loading }] = useMutation(CREATE_ORDER_MUTATION);
const qty = cartItems.map(({ quantity }) => {
return quantity;
});
const cartItemId = cartItems.map(({ id }) => {
return id;
});
function onSubmit() {
createOrder({
variables: {
qty: qty,
products: cartItemId,
paymentMethod: paymentMethod,
address: address,
},
})
}
Whenever I need to console someone. If you log the cartItemId, you'll get something like this:
Same goes with my qty.
Please let me know if you have any other questions or require any additional code, and I will gladly offer it.
Apollo Mutation:
const CREATE_ORDER_MUTATION = gql`
mutation createOrder(
$qty: Int!
$products: String!
$paymentMethod: String!
$address: String!
) {
createOrder(
orderedItems: [{ qty: $qty, products: $products }]
paymentMethod: $paymentMethod
address: $address
) {
id
orderedItems {
qty
products {
id
name
sku
description
}
}
}
}
`;
The code below will transform the cartItems into the desired result. You can loop through the cartItems and create an object with the required structure for each item.
const orderedItems = cartItems.map(({ id, quantity }) => {
return {
qty: quantity,
products: id,
};
});
Complete code will look something like this
const [cartItems, setCartItems] = useContext(CartContext);
const [createOrder, { data, loading }] = useMutation(CREATE_ORDER_MUTATION);
// Restructure the array to desired format
const orderedItems = cartItems.map(({ id, quantity }) => {
return {
qty: quantity,
products: id,
};
});
console.log(orderedItems); // To check if it looks correct
function onSubmit() {
createOrder({
variables: {
orderedItems: orderedItem,
paymentMethod: paymentMethod,
address: address,
},
})
}
I have two arrays, slicesRank and slicesCount with following structure. Each element has id and value, which is an array of 46. values is composed of date and measurement.
e.g. sliceRank[0]:
{id: Catan=rank, values(Array(46)) : {date, measurement} }
e.g. sliceCount[0]:
{id: Catan=count, values(Array(46)) : {date, measurement} }
What should I do if I want to combine the elements with the same prefix in id names. For example, the first element in this two arrays.
The desired sturcture would be
{id: Catan, values(Array(46)) : {date, count, rank} }
I tried the following, but the values shows undifined.
for(i=0; i<slicesRank.length; i++) {
var newElement = {};
newElement['id'] = slicesRank[i].id.replace('=rank', '');
newElement['values'] = {
date: slicesRank[i].date,
rank: slicesRank[i].measurement,
count: slicesCount[i].measurement
};
sliceNew.push(newElement);
};
The structure of slicesRank is like this:
Here's a sample transformation that you can try:
const combined = slicesRank.map((rank) => {
const id = rank.id.replace("=rank", "");
const count = slicesCount.find(
(count) => count.id.replace("=count", "") === id
);
return {
id,
values: rank.values.map((val, index) => ({
date: val.date,
rank: val.measurement,
count: count?.values[index].measurement
}))
};
});
Here's a working example
https://codesandbox.io/s/awesome-lovelace-e31zj?file=/src/index.js
By slice I think you mean Array.
const combinedArray = sliceRank.map((sliceItem, itemIndex) => {
return {
id,
values: sliceItem.values.map(({ date, measurement }, valueIndex) => ({
date,
count: sliceCount[itemIndex][valueIndex].measurement,
rank: measurement,
}))
}
})
This solution returns an array of objects combined using sliceRank and sliceCount. So now each array item now contains values of structure { date, count, rank }. Array.map is immutable so both slices will be the same.
You can try this.
let sliceRank=[{id: "Catan=rank", values : [{"date":"10/10/2020", "measurement":120} ]}];
let sliceCount=[{id: "Catan=count", values :[{"date":"10/10/2020", "measurement":20} ] }];
let newData=[];
const data = sliceRank.map((slice, sliceIndex) => {
return {
id:slice.id.split("=")[0],
values: slice.values.map(({ date, measurement }, valueIndex) => ({
date,
rank: measurement,
count: sliceCount[sliceIndex].values.map(r=> r.measurement)[0],
}))
}
})
console.log(data);
This question may be similar to In Angular2 *ngFor iteration, how do I output only unique values from the array? but my question is have some more features.
I have a list of the following form
let studentsList = [{
name:'A',
rollNo:1,
mark:10
},
{
name:'B',
rollNo:2,
mark:30
},
{
name:'C',
rollNo:3,
mark:40
},
{
name:'A',
rollNo:1,
mark:50
}
]
The objective is to display unique name and marks from the studentList and find the unique name from it like this.
A 10
B 30
C 40
Also if the name repeats then add the marks and display.
A 60
B 30
C 40
I could filter the unique names like this
import { Pipe, PipeTransform } from '#angular/core';
import * as _ from 'lodash';
#Pipe({
name: 'unique',
pure: false
})
export class UniquePipe implements PipeTransform {
transform(value: any): any{
if(value!== undefined && value!== null){
return _.uniqBy(value, 'name');
}
return value;
}
}
then in html
<ul>
<li *ngFor="let student of studentList| unique">
{{student .name}}
</li>
</ul>
Edit
The studentsList is dynamic we can add as many details needed.
If you need a result array of name and marks (sum of duplicates) then you can do that with a very cleaner way in lodash (since you tagged lodash). _.groupBy will always group it by its unique parameter and group all duplivates with that parameter inside a array for each group.
First, group it by name using _.groupBy
Then map (.map) each group and calculate sum of marks by .sumBy
Here is an working example:
let input = [{"name":"A","rollNo":1,"mark":10},{"name":"B","rollNo":2,"mark":30},{"name":"C","rollNo":3,"mark":40},{"name":"A","rollNo":1,"mark":50}],
res = _(input)
.groupBy('name')
.map((g, name) => ({name, mark: _.sumBy(g, 'mark')}))
.value();
console.log(res);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
You could use this function into your pipe:
value.reduce((acc, ele) => {
const existingStudent = acc.find(x => x.name === ele.name);
if(!existingStudent) return acc.concat(ele);
return (existingStudent.mark += ele.mark, acc);
},[])
Testing it:
var studentsList=[{name:"A",rollNo:1,mark:10},{name:"B",rollNo:2,mark:30},{name:"C",rollNo:3,mark:40},{name:"A",rollNo:1,mark:50}];
const resp = studentsList.reduce((acc, ele) => {
const existingStudent = acc.find(x => x.name === ele.name);
if(!existingStudent) return acc.concat(ele);
return (existingStudent.mark += ele.mark, acc);
},[])
console.log(resp);
An example how to accumulate your values.
let studentsList = [
{ name: 'A', rollNo: 1, mark: 10 },
{ name: 'B', rollNo: 2, mark: 30 },
{ name: 'C', rollNo: 3, mark: 40 },
{ name: 'A', rollNo: 1, mark: 50 }
];
const byName = {};
for (var student of studentsList) {
if (student.name in byName) {
byName[student.name].mark += student.mark;
} else {
byName[student.name] = { ...student };
}
}
console.log(Object.values(byName));
You can group your student array on the roll number and add the marks of same roll number in an object accumulator. Using array#values() get all the values corresponding to the roll number.
let studentsList = [{ name:'A', rollNo:1, mark:10 }, { name:'B', rollNo:2, mark:30 }, { name:'C', rollNo:3, mark:40 }, { name:'A', rollNo:1, mark:50 } ],
uniqueStudents = Object.values(studentsList.reduce((r,{name, rollNo, mark}) => {
r[rollNo] = r[rollNo] || {name, rollNo, total : 0};
r[rollNo].total += mark;
return r;
},{}));
console.log(uniqueStudents);