-3

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] }
]
vsync
  • 118,978
  • 58
  • 307
  • 400
Ali Haghighi
  • 143
  • 3
  • 11
  • 1
    *"How to match each text to its corresponding lines?"* That isn't *sorting*. – T.J. Crowder Sep 21 '21 at 07:15
  • Does this answer your question? [Sort array of objects by string property value](https://stackoverflow.com/questions/1129216/sort-array-of-objects-by-string-property-value) – Harshit Rastogi Sep 21 '21 at 07:17
  • yes. it's not sorting. How can I match each text to its related lines? – Ali Haghighi Sep 21 '21 at 07:20
  • @AliHaghighi - it's also not sorting by completely new array with a custom logic – vsync Sep 21 '21 at 07:21
  • It is not sorting!! – Mohamadamin Sep 21 '21 at 07:21
  • This is **not** the same as [this question](https://stackoverflow.com/q/64558230/104380), as this one is more complex as it deals with an array of objects and not just an object. it is similar but the solution is different enough to not mark it as dup. – vsync Sep 21 '21 at 07:57
  • [Step 1](/q/63542283/4642212), [Step 2](/q/64558230/4642212), [Step 3](/q/19874555/4642212). – Sebastian Simon Sep 21 '21 at 07:59
  • Does this answer your question? [Most efficient method to groupby on an array of objects](https://stackoverflow.com/questions/14446511/most-efficient-method-to-groupby-on-an-array-of-objects) (this is just a group-by operation) – pilchard Sep 21 '21 at 08:12

4 Answers4

3

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);
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
1

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 )
vsync
  • 118,978
  • 58
  • 307
  • 400
0

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);
Robby Cornelissen
  • 91,784
  • 22
  • 134
  • 156
0

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)