Mister Smth
Mister Smth

Reputation: 43

Algorithmic problem solution without using for loop

I am trying to solve the next case without using a for loop... Meaning trying to use only filter, reduce, map or combination of them.

I got an array of Objects:

const items = 
  [ { label: 1, count: 22, isRefined: false } 
  , { label: 2, count: 10, isRefined: false } 
  , { label: 3, count:  3, isRefined: false } 
  , { label: 4, count:  1, isRefined: false } 
  ]
 
const products = 
  [ { Locations: [{ LocationType: 1 } , { LocationType: 2 } , { LocationType: 3 } ]} 
  , { Locations: [{ LocationType: 1 } , { LocationType: 3 }                       ]} 
  , { Locations: [{ LocationType: 1 } , { LocationType: 3 } , { LocationType: 4 } ]} 
  , { Locations: [{ LocationType: 1 }                                             ]} 
  , { Locations: [{ LocationType: 1 } , { LocationType: 2 } , { LocationType: 3 } ]} 
  ]

The case is next:

I need to overwrite each item's inner Object count key with the value of corresponding products with the Objects in which the Locations objects contain the matching Location Type to the items object label...

For example, the output here should be

const output = 
  [ { label: 1, count: 5, isRefined: false } 
  , { label: 2, count: 2, isRefined: false } 
  , { label: 3, count: 4, isRefined: false } 
  , { label: 4, count: 1, isRefined: false } 
  ] 

I am totally stuck on trying to come to a solution without using a for loop or any kind of for loop, help would be much appreciated! Thank you

Upvotes: 0

Views: 151

Answers (4)

Sharon Choong
Sharon Choong

Reputation: 596

In case forEach is not an option, here is another solution that uses map, reduce and filter.

const result = items.map(d => {
    const locationCount = products.reduce((count, p) => count + p.Locations.filter(l => l.LocationType == d.label).length, 0);
    let item = Object.assign({}, d);
    item.count = locationCount;
    return item;
 })

For example:

const items = 
  [ { label: 1, count: 22, isRefined: false } 
  , { label: 2, count: 10, isRefined: false } 
  , { label: 3, count:  3, isRefined: false } 
  , { label: 4, count:  1, isRefined: false } 
  ] 
const products = 
  [ { Locations: [{ LocationType: 1 } , { LocationType: 2 } , { LocationType: 3 } ]} 
  , { Locations: [{ LocationType: 1 } , { LocationType: 3 }                       ]} 
  , { Locations: [{ LocationType: 1 } , { LocationType: 3 } , { LocationType: 4 } ]} 
  , { Locations: [{ LocationType: 1 }                                             ]} 
  , { Locations: [{ LocationType: 1 } , { LocationType: 2 } , { LocationType: 3 } ]} 
  ] 

const result = items.map(d => {
    const locationCount = products.reduce((count, p) => count + p.Locations.filter(l => l.LocationType == d.label).length, 0);
    let item = Object.assign({}, d);
    item.count = locationCount;
    return item;
})

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Upvotes: 0

Mister Jojo
Mister Jojo

Reputation: 22355

my way...

const countP = v => products.filter(p=>p.Locations.some(x=>x.LocationType===v)).length

const output = items.map(el=>({...el, count: countP(el.label)}))

proof:

const items = 
  [ { label: 1, count: 22, isRefined: false } 
  , { label: 2, count: 10, isRefined: false } 
  , { label: 3, count:  3, isRefined: false } 
  , { label: 4, count:  1, isRefined: false } 
  ] 

const products = 
  [ { Locations: [{ LocationType: 1 } , { LocationType: 2 } , { LocationType: 3 } ]} 
  , { Locations: [{ LocationType: 1 } , { LocationType: 3 }                       ]} 
  , { Locations: [{ LocationType: 1 } , { LocationType: 3 } , { LocationType: 4 } ]} 
  , { Locations: [{ LocationType: 1 }                                             ]} 
  , { Locations: [{ LocationType: 1 } , { LocationType: 2 } , { LocationType: 3 } ]} 
  ] 

const countP = v => products.filter(p=>p.Locations.some(x=>x.LocationType===v)).length

const output = items.map(el=>({...el, count: countP(el.label)}))  
console.log( output )
.as-console-wrapper { max-height: 100% !important; top: 0; }
.as-console-row         { background-color: #87f1f1; }
.as-console-row::after  { display:none !important; }

Upvotes: 0

Reqven
Reqven

Reputation: 1778

Simple, and elegant.

UPDATE
Thanks to @plichard for suggesting in the comments below to use the some() method and object destructuring in the filter callback. It's really neat that way !

const output = items.map(item => {
  const matches = (location) => location.LocationType == item.label;
  const filtered = products.filter(({ Locations }) => Locations.some(matches));
  return { ...item, count: filtered.length };
});

Upvotes: 1

ibrahim mahrir
ibrahim mahrir

Reputation: 31712

First calculate the counts (the number of times each location type appears in the products array), preferably as an object where the keys are the location types and the values are the counts. For that a simple reduce/forEach will do the job like so:

let counts = products.reduce((counts, locs) => {
  locs.Locations.forEach(loc =>
    counts[loc.LocationType] = (counts[loc.LocationType] || 0) + 1
  );
  return counts;
}, {});

Then simply map the items array into a new array where you shallow-copy the objects using a spread syntax and update the count property using the counts object calculated earlier:

const newItems = items.map(item => ({
  ...item,
  count: counts[item.label] || 0
}));

And if you want to alter the original objects themselves instead, then a simple forEach will suffice:

items.forEach(item => item.count = counts[item.label] || 0);

The || 0 part is a fallback value in case the item doesn't have any location types in the products array, in which case counts[item.label] will be undefined and 0 will be used instead. This is called short-circuit evaluation.

Demo:

const items = [{label: 1, count: 22, isRefined: false}, {label: 2, count: 10, isRefined: false}, {label: 3, count: 3, isRefined: false},{label: 4, count: 1, isRefined: false}];

const products = [{Locations:[{LocationType:1},{LocationType:2},{LocationType:3}]},{Locations:[{LocationType:1},{LocationType:3}]},{Locations:[{LocationType:1},{LocationType:3},{LocationType:4}]},{Locations:[{LocationType:1}]},{Locations:[{LocationType:1},{LocationType:2},{LocationType:3}]}];



let counts = products.reduce((counts, locs) => {
  locs.Locations.forEach(loc =>
    counts[loc.LocationType] = (counts[loc.LocationType] || 0) + 1
  );
  return counts;
}, {});

const newItems = items.map(item => ({
  ...item,
  count: counts[item.label] || 0
}));

console.log(newItems);

Upvotes: 0

Related Questions