Home > Enterprise >  How to group javascript array based on some property?
How to group javascript array based on some property?

Time:09-22

I have a JS array of objects like this:

var myArray = [
  { line: 20, text: [31, 80] },
  { line: 10, text: [80, 22] }
]

lines are unique in entire myArray , each line has some texts (which are not unique). How to match each text to its corresponding lines?

The final result should be like this:

var myNewArray = [
  { text: 31, line: [20] },
  { text: 80, line: [20, 10] },
  { text: 22, line: [10] }
]

CodePudding user response:

Some approaches with Map.

As result you get a temporary map which collects all text, grouped by line. To get an array of objects, map the key/values pairs as eanted properties.

  • Because of having nested array of the data, you need eiter to normalize the data to get single line/text values and then add a grouping by text,

    const
        data = [{ line: 20, text: [31, 80] }, { line: 10, text: [80, 22] }],
        result = Array.from(
            data
                .flatMap(({ line, text }) => text.map(text => ({ text, line })))
                .reduce((m, { text, line }) => m.set(text, [...(m.get(text) || []), line]), new Map),
            ([text, line]) => ({ text, line })
        );
    
    console.log(result);

  • Or do it in a single step but with a nested approach of reducing the outer (line) and inner arrays (text arrays).

    const
        data = [
            { line: 20, text: [31, 80] },
            { line: 10, text: [80, 22] }
        ],
        result = Array.from(
            data.reduce(
                (m, { line, text }) => 
                    text.reduce(
                        (n, text) => n.set(text, [...(n.get(text) || []), line]),
                        m
                    ),
                new Map
            ),
            ([text, line]) => ({ text, line })
        );
    
    console.log(result);

CodePudding user response:

Here's how:

var myArray = [
   { line: 20, text: [31, 80] },
   { line: 10, text: [80, 22] }
]

var newArray = myArray.reduce((acc, {line, text}) => {
  for( let t of text ){
    const match = acc.find(({text}) => text == t) // check if the text already exists in newArray 
    if( match ) match.lines.push(line) // if exists, add the line to that text
    else acc.push({text:t, lines:[line]}) // it not, create a new object with that line
  }
  return acc
}, [])

console.log( newArray )

Or by first generating an Object instead of an Array, which is faster if your dataset is huge, and then convert that to an Array at the end:

var myArray = [
   { line: 20, text: [31, 80] },
   { line: 10, text: [80, 22] }
]

// generate a key/value pairs for text/lines
var newArray = myArray.reduce((acc, {line, text}) => {
  for( let t of text )
    acc[t] = [...(acc[t] || []), line]
  return acc
}, {})

// convert the above Object to an Array of Objects (AKA Collection)
newArray = Object.entries(newArray).map(([text,lines]) => ({text, lines}))

console.log( newArray )

CodePudding user response:

Probably easiest by first building an intermediate Map that indexes lines by text:

const data = [
  {line: 20, text: [31,80]},
  {line: 10, text: [80,22]}
];

const result = [...data.reduce((map, {line, text}) => {
  text.forEach(t => {
    map.has(t) || map.set(t, []);
    map.get(t).push(line);
  });
  return map;
}, new Map()).entries()].map(([text, line]) => ({text, line}));

console.log(result);

CodePudding user response:

Here is a simple solution to your problem.

var myArray = [
  { line: 20, text: [31, 80] },
  { line: 10, text: [80, 22] }
]

var myNewArray = []

myArray.forEach((item) => {
  item.text.forEach((t) => {
    const index = myNewArray.findIndex((e => e.text === t))
    index === -1
      ? myNewArray.push({text: t, line: [item.line]}) 
      : myNewArray[index].line.push(item.line)
  })
})

console.log(myNewArray)

  • Related